]> Pileus Git - ~andy/linux/blobdiff - drivers/gpu/drm/radeon/r600_cs.c
Merge branch 'for-linus' of git://oss.sgi.com/xfs/xfs
[~andy/linux] / drivers / gpu / drm / radeon / r600_cs.c
index dd009da0e7a0c1c47b50f290a868a8e50d730072..17e42195c632eab62ddef0db40aaf713c9fc067f 100644 (file)
@@ -28,7 +28,6 @@
 #include "drmP.h"
 #include "radeon.h"
 #include "r600d.h"
-#include "avivod.h"
 
 static int r600_cs_packet_next_reloc_mm(struct radeon_cs_parser *p,
                                        struct radeon_cs_reloc **cs_reloc);
@@ -177,13 +176,136 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p,
        return 0;
 }
 
+/**
+ * r600_cs_packet_next_vline() - parse userspace VLINE packet
+ * @parser:            parser structure holding parsing context.
+ *
+ * Userspace sends a special sequence for VLINE waits.
+ * PACKET0 - VLINE_START_END + value
+ * PACKET3 - WAIT_REG_MEM poll vline status reg
+ * RELOC (P3) - crtc_id in reloc.
+ *
+ * This function parses this and relocates the VLINE START END
+ * and WAIT_REG_MEM packets to the correct crtc.
+ * It also detects a switched off crtc and nulls out the
+ * wait in that case.
+ */
+static int r600_cs_packet_parse_vline(struct radeon_cs_parser *p)
+{
+       struct drm_mode_object *obj;
+       struct drm_crtc *crtc;
+       struct radeon_crtc *radeon_crtc;
+       struct radeon_cs_packet p3reloc, wait_reg_mem;
+       int crtc_id;
+       int r;
+       uint32_t header, h_idx, reg, wait_reg_mem_info;
+       volatile uint32_t *ib;
+
+       ib = p->ib->ptr;
+
+       /* parse the WAIT_REG_MEM */
+       r = r600_cs_packet_parse(p, &wait_reg_mem, p->idx);
+       if (r)
+               return r;
+
+       /* check its a WAIT_REG_MEM */
+       if (wait_reg_mem.type != PACKET_TYPE3 ||
+           wait_reg_mem.opcode != PACKET3_WAIT_REG_MEM) {
+               DRM_ERROR("vline wait missing WAIT_REG_MEM segment\n");
+               r = -EINVAL;
+               return r;
+       }
+
+       wait_reg_mem_info = radeon_get_ib_value(p, wait_reg_mem.idx + 1);
+       /* bit 4 is reg (0) or mem (1) */
+       if (wait_reg_mem_info & 0x10) {
+               DRM_ERROR("vline WAIT_REG_MEM waiting on MEM rather than REG\n");
+               r = -EINVAL;
+               return r;
+       }
+       /* waiting for value to be equal */
+       if ((wait_reg_mem_info & 0x7) != 0x3) {
+               DRM_ERROR("vline WAIT_REG_MEM function not equal\n");
+               r = -EINVAL;
+               return r;
+       }
+       if ((radeon_get_ib_value(p, wait_reg_mem.idx + 2) << 2) != AVIVO_D1MODE_VLINE_STATUS) {
+               DRM_ERROR("vline WAIT_REG_MEM bad reg\n");
+               r = -EINVAL;
+               return r;
+       }
+
+       if (radeon_get_ib_value(p, wait_reg_mem.idx + 5) != AVIVO_D1MODE_VLINE_STAT) {
+               DRM_ERROR("vline WAIT_REG_MEM bad bit mask\n");
+               r = -EINVAL;
+               return r;
+       }
+
+       /* jump over the NOP */
+       r = r600_cs_packet_parse(p, &p3reloc, p->idx + wait_reg_mem.count + 2);
+       if (r)
+               return r;
+
+       h_idx = p->idx - 2;
+       p->idx += wait_reg_mem.count + 2;
+       p->idx += p3reloc.count + 2;
+
+       header = radeon_get_ib_value(p, h_idx);
+       crtc_id = radeon_get_ib_value(p, h_idx + 2 + 7 + 1);
+       reg = CP_PACKET0_GET_REG(header);
+       mutex_lock(&p->rdev->ddev->mode_config.mutex);
+       obj = drm_mode_object_find(p->rdev->ddev, crtc_id, DRM_MODE_OBJECT_CRTC);
+       if (!obj) {
+               DRM_ERROR("cannot find crtc %d\n", crtc_id);
+               r = -EINVAL;
+               goto out;
+       }
+       crtc = obj_to_crtc(obj);
+       radeon_crtc = to_radeon_crtc(crtc);
+       crtc_id = radeon_crtc->crtc_id;
+
+       if (!crtc->enabled) {
+               /* if the CRTC isn't enabled - we need to nop out the WAIT_REG_MEM */
+               ib[h_idx + 2] = PACKET2(0);
+               ib[h_idx + 3] = PACKET2(0);
+               ib[h_idx + 4] = PACKET2(0);
+               ib[h_idx + 5] = PACKET2(0);
+               ib[h_idx + 6] = PACKET2(0);
+               ib[h_idx + 7] = PACKET2(0);
+               ib[h_idx + 8] = PACKET2(0);
+       } else if (crtc_id == 1) {
+               switch (reg) {
+               case AVIVO_D1MODE_VLINE_START_END:
+                       header &= ~R600_CP_PACKET0_REG_MASK;
+                       header |= AVIVO_D2MODE_VLINE_START_END >> 2;
+                       break;
+               default:
+                       DRM_ERROR("unknown crtc reloc\n");
+                       r = -EINVAL;
+                       goto out;
+               }
+               ib[h_idx] = header;
+               ib[h_idx + 4] = AVIVO_D2MODE_VLINE_STATUS >> 2;
+       }
+out:
+       mutex_unlock(&p->rdev->ddev->mode_config.mutex);
+       return r;
+}
+
 static int r600_packet0_check(struct radeon_cs_parser *p,
                                struct radeon_cs_packet *pkt,
                                unsigned idx, unsigned reg)
 {
+       int r;
+
        switch (reg) {
        case AVIVO_D1MODE_VLINE_START_END:
-       case AVIVO_D2MODE_VLINE_START_END:
+               r = r600_cs_packet_parse_vline(p);
+               if (r) {
+                       DRM_ERROR("No reloc for ib[%d]=0x%04X\n",
+                                       idx, reg);
+                       return r;
+               }
                break;
        default:
                printk(KERN_ERR "Forbidden register 0x%04X in cs at %d\n",
@@ -257,7 +379,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                        return -EINVAL;
                }
                ib[idx+0] = idx_value + (u32)(reloc->lobj.gpu_offset & 0xffffffff);
-               ib[idx+1] = upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+               ib[idx+1] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
                break;
        case PACKET3_DRAW_INDEX_AUTO:
                if (pkt->count != 1) {
@@ -285,7 +407,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                                return -EINVAL;
                        }
                        ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
-                       ib[idx+2] = upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+                       ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
                }
                break;
        case PACKET3_SURFACE_SYNC:
@@ -316,7 +438,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                                return -EINVAL;
                        }
                        ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
-                       ib[idx+2] |= upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+                       ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
                }
                break;
        case PACKET3_EVENT_WRITE_EOP:
@@ -330,7 +452,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                        return -EINVAL;
                }
                ib[idx+1] += (u32)(reloc->lobj.gpu_offset & 0xffffffff);
-               ib[idx+2] |= upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+               ib[idx+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
                break;
        case PACKET3_SET_CONFIG_REG:
                start_reg = (idx_value << 2) + PACKET3_SET_CONFIG_REG_OFFSET;
@@ -452,7 +574,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
                                        return -EINVAL;
                                }
                                ib[idx+1+(i*7)+0] += (u32)((reloc->lobj.gpu_offset) & 0xffffffff);
-                               ib[idx+1+(i*7)+2] |= upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
+                               ib[idx+1+(i*7)+2] += upper_32_bits(reloc->lobj.gpu_offset) & 0xff;
                                break;
                        case SQ_TEX_VTX_INVALID_TEXTURE:
                        case SQ_TEX_VTX_INVALID_BUFFER:
@@ -599,6 +721,8 @@ static void r600_cs_parser_fini(struct radeon_cs_parser *parser, int error)
        kfree(parser->relocs);
        for (i = 0; i < parser->nchunks; i++) {
                kfree(parser->chunks[i].kdata);
+               kfree(parser->chunks[i].kpage[0]);
+               kfree(parser->chunks[i].kpage[1]);
        }
        kfree(parser->chunks);
        kfree(parser->chunks_array);