#include #include #include #include #include #include #include #include #include #include #include using namespace std; using namespace kms; /* * V4L2 and DRM differ in their interpretation of YUV420::NV12 * * V4L2 NV12 is a Y and UV co-located planes in a single plane buffer. * DRM NV12 is a Y and UV planes presented as dual plane buffer, * which is known as NM12 in V4L2. * * Since here we have hybrid DRM/V4L2 user space helper functions * we need to translate DRM::NV12 to V4L2:NM12 pixel format back * and forth to keep the data view consistent. */ /* V4L2 helper funcs */ static vector v4l2_get_formats(int fd, uint32_t buf_type) { vector v; v4l2_fmtdesc desc{}; desc.type = buf_type; while (ioctl(fd, VIDIOC_ENUM_FMT, &desc) == 0) { if (desc.pixelformat == V4L2_PIX_FMT_NV12M) v.push_back(PixelFormat::NV12); else if (desc.pixelformat != V4L2_PIX_FMT_NV12) v.push_back((PixelFormat)desc.pixelformat); desc.index++; } return v; } static void v4l2_set_format(int fd, PixelFormat fmt, uint32_t width, uint32_t height, uint32_t buf_type) { int r; v4l2_format v4lfmt{}; v4lfmt.type = buf_type; r = ioctl(fd, VIDIOC_G_FMT, &v4lfmt); ASSERT(r == 0); const PixelFormatInfo& pfi = get_pixel_format_info(fmt); bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; if (mplane) { v4l2_pix_format_mplane& mp = v4lfmt.fmt.pix_mp; uint32_t used_fmt; if (fmt == PixelFormat::NV12) used_fmt = V4L2_PIX_FMT_NV12M; else used_fmt = (uint32_t)fmt; mp.pixelformat = used_fmt; mp.width = width; mp.height = height; mp.num_planes = pfi.num_planes; for (unsigned i = 0; i < pfi.num_planes; ++i) { const PixelFormatPlaneInfo& pfpi = pfi.planes[i]; v4l2_plane_pix_format& p = mp.plane_fmt[i]; p.bytesperline = width * pfpi.bitspp / 8; p.sizeimage = p.bytesperline * height / pfpi.ysub; } r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt); ASSERT(r == 0); ASSERT(mp.pixelformat == used_fmt); ASSERT(mp.width == width); ASSERT(mp.height == height); ASSERT(mp.num_planes == pfi.num_planes); for (unsigned i = 0; i < pfi.num_planes; ++i) { const PixelFormatPlaneInfo& pfpi = pfi.planes[i]; v4l2_plane_pix_format& p = mp.plane_fmt[i]; ASSERT(p.bytesperline == width * pfpi.bitspp / 8); ASSERT(p.sizeimage == p.bytesperline * height / pfpi.ysub); } } else { ASSERT(pfi.num_planes == 1); v4lfmt.fmt.pix.pixelformat = (uint32_t)fmt; v4lfmt.fmt.pix.width = width; v4lfmt.fmt.pix.height = height; v4lfmt.fmt.pix.bytesperline = width * pfi.planes[0].bitspp / 8; r = ioctl(fd, VIDIOC_S_FMT, &v4lfmt); ASSERT(r == 0); ASSERT(v4lfmt.fmt.pix.pixelformat == (uint32_t)fmt); ASSERT(v4lfmt.fmt.pix.width == width); ASSERT(v4lfmt.fmt.pix.height == height); ASSERT(v4lfmt.fmt.pix.bytesperline == width * pfi.planes[0].bitspp / 8); } } static void v4l2_get_selection(int fd, uint32_t& left, uint32_t& top, uint32_t& width, uint32_t& height, uint32_t buf_type) { int r; struct v4l2_selection selection; if (buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { selection.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; selection.target = V4L2_SEL_TGT_CROP; } else if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE || buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; selection.target = V4L2_SEL_TGT_COMPOSE; } else { FAIL("buf_type (%d) is not valid\n", buf_type); } r = ioctl(fd, VIDIOC_G_SELECTION, &selection); ASSERT(r == 0); left = selection.r.left; top = selection.r.top; width = selection.r.width; height = selection.r.height; } static void v4l2_set_selection(int fd, uint32_t& left, uint32_t& top, uint32_t& width, uint32_t& height, uint32_t buf_type) { int r; struct v4l2_selection selection; if (buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { selection.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; selection.target = V4L2_SEL_TGT_CROP; } else if (buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE || buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) { selection.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; selection.target = V4L2_SEL_TGT_COMPOSE; } else { FAIL("buf_type (%d) is not valid\n", buf_type); } selection.r.left = left; selection.r.top = top; selection.r.width = width; selection.r.height = height; r = ioctl(fd, VIDIOC_S_SELECTION, &selection); ASSERT(r == 0); left = selection.r.left; top = selection.r.top; width = selection.r.width; height = selection.r.height; } static void v4l2_request_bufs(int fd, uint32_t queue_size, uint32_t buf_type) { v4l2_requestbuffers v4lreqbuf{}; v4lreqbuf.type = buf_type; v4lreqbuf.memory = V4L2_MEMORY_DMABUF; v4lreqbuf.count = queue_size; int r = ioctl(fd, VIDIOC_REQBUFS, &v4lreqbuf); ASSERT(r == 0); ASSERT(v4lreqbuf.count == queue_size); } static void v4l2_queue_dmabuf(int fd, uint32_t index, DumbFramebuffer* fb, uint32_t buf_type) { v4l2_buffer buf{}; buf.type = buf_type; buf.memory = V4L2_MEMORY_DMABUF; buf.index = index; const PixelFormatInfo& pfi = get_pixel_format_info(fb->format()); bool mplane = buf_type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; if (mplane) { buf.length = pfi.num_planes; v4l2_plane planes[4]{}; buf.m.planes = planes; for (unsigned i = 0; i < pfi.num_planes; ++i) { planes[i].m.fd = fb->prime_fd(i); planes[i].bytesused = fb->size(i); planes[i].length = fb->size(i); } int r = ioctl(fd, VIDIOC_QBUF, &buf); ASSERT(r == 0); } else { buf.m.fd = fb->prime_fd(0); int r = ioctl(fd, VIDIOC_QBUF, &buf); ASSERT(r == 0); } } static uint32_t v4l2_dequeue(int fd, uint32_t buf_type) { v4l2_buffer buf{}; buf.type = buf_type; buf.memory = V4L2_MEMORY_DMABUF; // V4L2 crashes if planes are not set v4l2_plane planes[4]{}; buf.m.planes = planes; buf.length = 4; int r = ioctl(fd, VIDIOC_DQBUF, &buf); if (r) throw system_error(errno, generic_category()); return buf.index; } VideoDevice::VideoDevice(const string& dev) : VideoDevice(::open(dev.c_str(), O_RDWR | O_NONBLOCK)) { } VideoDevice::VideoDevice(int fd) : m_fd(fd), m_has_capture(false), m_has_output(false), m_has_m2m(false) { if (fd < 0) throw runtime_error("bad fd"); struct v4l2_capability cap = {}; int r = ioctl(fd, VIDIOC_QUERYCAP, &cap); ASSERT(r == 0); if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) { m_has_capture = true; m_has_mplane_capture = true; } else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) { m_has_capture = true; m_has_mplane_capture = false; } if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) { m_has_output = true; m_has_mplane_output = true; } else if (cap.capabilities & V4L2_CAP_VIDEO_OUTPUT) { m_has_output = true; m_has_mplane_output = false; } if (cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) { m_has_m2m = true; m_has_capture = true; m_has_output = true; m_has_mplane_m2m = true; m_has_mplane_capture = true; m_has_mplane_output = true; } else if (cap.capabilities & V4L2_CAP_VIDEO_M2M) { m_has_m2m = true; m_has_capture = true; m_has_output = true; m_has_mplane_m2m = false; m_has_mplane_capture = false; m_has_mplane_output = false; } } VideoDevice::~VideoDevice() { ::close(m_fd); } VideoStreamer* VideoDevice::get_capture_streamer() { ASSERT(m_has_capture); if (!m_capture_streamer) { auto type = m_has_mplane_capture ? VideoStreamer::StreamerType::CaptureMulti : VideoStreamer::StreamerType::CaptureSingle; m_capture_streamer = std::unique_ptr(new VideoStreamer(m_fd, type)); } return m_capture_streamer.get(); } VideoStreamer* VideoDevice::get_output_streamer() { ASSERT(m_has_output); if (!m_output_streamer) { auto type = m_has_mplane_output ? VideoStreamer::StreamerType::OutputMulti : VideoStreamer::StreamerType::OutputSingle; m_output_streamer = std::unique_ptr(new VideoStreamer(m_fd, type)); } return m_output_streamer.get(); } vector> VideoDevice::get_discrete_frame_sizes(PixelFormat fmt) { vector> v; v4l2_frmsizeenum v4lfrms{}; v4lfrms.pixel_format = (uint32_t)fmt; int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms); ASSERT(r); FAIL_IF(v4lfrms.type != V4L2_FRMSIZE_TYPE_DISCRETE, "No discrete frame sizes"); while (ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms) == 0) { v.emplace_back(v4lfrms.discrete.width, v4lfrms.discrete.height); v4lfrms.index++; }; return v; } VideoDevice::VideoFrameSize VideoDevice::get_frame_sizes(PixelFormat fmt) { v4l2_frmsizeenum v4lfrms{}; v4lfrms.pixel_format = (uint32_t)fmt; int r = ioctl(m_fd, VIDIOC_ENUM_FRAMESIZES, &v4lfrms); ASSERT(r); FAIL_IF(v4lfrms.type == V4L2_FRMSIZE_TYPE_DISCRETE, "No continuous frame sizes"); VideoFrameSize s; s.min_w = v4lfrms.stepwise.min_width; s.max_w = v4lfrms.stepwise.max_width; s.step_w = v4lfrms.stepwise.step_width; s.min_h = v4lfrms.stepwise.min_height; s.max_h = v4lfrms.stepwise.max_height; s.step_h = v4lfrms.stepwise.step_height; return s; } vector VideoDevice::get_capture_devices() { vector v; for (int i = 0; i < 20; ++i) { string name = "/dev/video" + to_string(i); struct stat buffer; if (stat(name.c_str(), &buffer) != 0) continue; try { VideoDevice vid(name); if (vid.has_capture() && !vid.has_m2m()) v.push_back(name); } catch (...) { } } return v; } vector VideoDevice::get_m2m_devices() { vector v; for (int i = 0; i < 20; ++i) { string name = "/dev/video" + to_string(i); struct stat buffer; if (stat(name.c_str(), &buffer) != 0) continue; try { VideoDevice vid(name); if (vid.has_m2m()) v.push_back(name); } catch (...) { } } return v; } VideoStreamer::VideoStreamer(int fd, StreamerType type) : m_fd(fd), m_type(type) { } std::vector VideoStreamer::get_ports() { vector v; switch (m_type) { case StreamerType::CaptureSingle: case StreamerType::CaptureMulti: { struct v4l2_input input { }; while (ioctl(m_fd, VIDIOC_ENUMINPUT, &input) == 0) { v.push_back(string((char*)&input.name)); input.index++; } break; } case StreamerType::OutputSingle: case StreamerType::OutputMulti: { struct v4l2_output output { }; while (ioctl(m_fd, VIDIOC_ENUMOUTPUT, &output) == 0) { v.push_back(string((char*)&output.name)); output.index++; } break; } default: FAIL("Bad StreamerType"); } return v; } void VideoStreamer::set_port(uint32_t index) { unsigned long req; switch (m_type) { case StreamerType::CaptureSingle: case StreamerType::CaptureMulti: req = VIDIOC_S_INPUT; break; case StreamerType::OutputSingle: case StreamerType::OutputMulti: req = VIDIOC_S_OUTPUT; break; default: FAIL("Bad StreamerType"); } int r = ioctl(m_fd, req, &index); ASSERT(r == 0); } static v4l2_buf_type get_buf_type(VideoStreamer::StreamerType type) { switch (type) { case VideoStreamer::StreamerType::CaptureSingle: return V4L2_BUF_TYPE_VIDEO_CAPTURE; case VideoStreamer::StreamerType::CaptureMulti: return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; case VideoStreamer::StreamerType::OutputSingle: return V4L2_BUF_TYPE_VIDEO_OUTPUT; case VideoStreamer::StreamerType::OutputMulti: return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; default: FAIL("Bad StreamerType"); } } std::vector VideoStreamer::get_formats() { return v4l2_get_formats(m_fd, get_buf_type(m_type)); } void VideoStreamer::set_format(PixelFormat fmt, uint32_t width, uint32_t height) { v4l2_set_format(m_fd, fmt, width, height, get_buf_type(m_type)); } void VideoStreamer::get_selection(uint32_t& left, uint32_t& top, uint32_t& width, uint32_t& height) { v4l2_get_selection(m_fd, left, top, width, height, get_buf_type(m_type)); } void VideoStreamer::set_selection(uint32_t& left, uint32_t& top, uint32_t& width, uint32_t& height) { v4l2_set_selection(m_fd, left, top, width, height, get_buf_type(m_type)); } void VideoStreamer::set_queue_size(uint32_t queue_size) { v4l2_request_bufs(m_fd, queue_size, get_buf_type(m_type)); m_fbs.resize(queue_size); } void VideoStreamer::queue(DumbFramebuffer* fb) { uint32_t idx; for (idx = 0; idx < m_fbs.size(); ++idx) { if (m_fbs[idx] == nullptr) break; } FAIL_IF(idx == m_fbs.size(), "queue full"); m_fbs[idx] = fb; v4l2_queue_dmabuf(m_fd, idx, fb, get_buf_type(m_type)); } DumbFramebuffer* VideoStreamer::dequeue() { uint32_t idx = v4l2_dequeue(m_fd, get_buf_type(m_type)); auto fb = m_fbs[idx]; m_fbs[idx] = nullptr; return fb; } void VideoStreamer::stream_on() { uint32_t buf_type = get_buf_type(m_type); int r = ioctl(m_fd, VIDIOC_STREAMON, &buf_type); FAIL_IF(r, "Failed to enable stream: %d", r); } void VideoStreamer::stream_off() { uint32_t buf_type = get_buf_type(m_type); int r = ioctl(m_fd, VIDIOC_STREAMOFF, &buf_type); FAIL_IF(r, "Failed to disable stream: %d", r); } 313'>313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
/* r128_drv.h -- Private header for r128 driver -*- linux-c -*-
 * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com
 *
 * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
 * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice (including the next
 * paragraph) shall be included in all copies or substantial portions of the
 * Software.
 *
 * 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
 * PRECISION INSIGHT AND/OR ITS SUPPLIERS 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.
 *
 * Authors:
 *    Rickard E. (Rik) Faith <faith@valinux.com>
 *    Kevin E. Martin <martin@valinux.com>
 *    Gareth Hughes <gareth@valinux.com>
 *    Michel Dänzer <daenzerm@student.ethz.ch>
 */

#ifndef __R128_DRV_H__
#define __R128_DRV_H__

#define GET_RING_HEAD(ring)		DRM_READ32(  (ring)->ring_rptr, 0 ) /* (ring)->head */
#define SET_RING_HEAD(ring,val)		DRM_WRITE32( (ring)->ring_rptr, 0, (val) ) /* (ring)->head */

typedef struct drm_r128_freelist {
   	unsigned int age;
   	drm_buf_t *buf;
   	struct drm_r128_freelist *next;
   	struct drm_r128_freelist *prev;
} drm_r128_freelist_t;

typedef struct drm_r128_ring_buffer {
	u32 *start;
	u32 *end;
	int size;
	int size_l2qw;

	volatile u32 *head;
	u32 tail;
	u32 tail_mask;
	int space;

	int high_mark;
	drm_local_map_t *ring_rptr;
} drm_r128_ring_buffer_t;

typedef struct drm_r128_private {
	drm_r128_ring_buffer_t ring;
	drm_r128_sarea_t *sarea_priv;

	int cce_mode;
	int cce_fifo_size;
	int cce_running;

   	drm_r128_freelist_t *head;
   	drm_r128_freelist_t *tail;

	int usec_timeout;
	int is_pci;
	unsigned long phys_pci_gart;
	dma_addr_t bus_pci_gart;
	unsigned long cce_buffers_offset;

	atomic_t idle_count;

	int page_flipping;
	int current_page;
	u32 crtc_offset;
	u32 crtc_offset_cntl;

	u32 color_fmt;
	unsigned int front_offset;
	unsigned int front_pitch;
	unsigned int back_offset;
	unsigned int back_pitch;

	u32 depth_fmt;
	unsigned int depth_offset;
	unsigned int depth_pitch;
	unsigned int span_offset;

	u32 front_pitch_offset_c;
	u32 back_pitch_offset_c;
	u32 depth_pitch_offset_c;
	u32 span_pitch_offset_c;

	drm_local_map_t *sarea;
	drm_local_map_t *fb;
	drm_local_map_t *mmio;
	drm_local_map_t *cce_ring;
	drm_local_map_t *ring_rptr;
	drm_local_map_t *buffers;
	drm_local_map_t *agp_textures;
} drm_r128_private_t;

typedef struct drm_r128_buf_priv {
	u32 age;
	int prim;
	int discard;
	int dispatched;
   	drm_r128_freelist_t *list_entry;
} drm_r128_buf_priv_t;

				/* r128_cce.c */
extern int r128_cce_init( DRM_IOCTL_ARGS );
extern int r128_cce_start( DRM_IOCTL_ARGS );
extern int r128_cce_stop( DRM_IOCTL_ARGS );
extern int r128_cce_reset( DRM_IOCTL_ARGS );
extern int r128_cce_idle( DRM_IOCTL_ARGS );
extern int r128_engine_reset( DRM_IOCTL_ARGS );
extern int r128_fullscreen( DRM_IOCTL_ARGS );
extern int r128_cce_buffers( DRM_IOCTL_ARGS );
extern int r128_getparam( DRM_IOCTL_ARGS );

extern void r128_freelist_reset( drm_device_t *dev );
extern drm_buf_t *r128_freelist_get( drm_device_t *dev );

extern int r128_wait_ring( drm_r128_private_t *dev_priv, int n );

static __inline__ void
r128_update_ring_snapshot( drm_r128_ring_buffer_t *ring )
{
	ring->space = (GET_RING_HEAD( ring ) - ring->tail) * sizeof(u32);
	if ( ring->space <= 0 )
		ring->space += ring->size;
}

extern int r128_do_cce_idle( drm_r128_private_t *dev_priv );
extern int r128_do_cleanup_cce( drm_device_t *dev );
extern int r128_do_cleanup_pageflip( drm_device_t *dev );

				/* r128_state.c */
extern int r128_cce_clear( DRM_IOCTL_ARGS );
extern int r128_cce_swap( DRM_IOCTL_ARGS );
extern int r128_cce_flip( DRM_IOCTL_ARGS );
extern int r128_cce_vertex( DRM_IOCTL_ARGS );
extern int r128_cce_indices( DRM_IOCTL_ARGS );
extern int r128_cce_blit( DRM_IOCTL_ARGS );
extern int r128_cce_depth( DRM_IOCTL_ARGS );
extern int r128_cce_stipple( DRM_IOCTL_ARGS );
extern int r128_cce_indirect( DRM_IOCTL_ARGS );


/* Register definitions, register access macros and drmAddMap constants
 * for Rage 128 kernel driver.
 */

#define R128_AUX_SC_CNTL		0x1660
#	define R128_AUX1_SC_EN			(1 << 0)
#	define R128_AUX1_SC_MODE_OR		(0 << 1)
#	define R128_AUX1_SC_MODE_NAND		(1 << 1)
#	define R128_AUX2_SC_EN			(1 << 2)
#	define R128_AUX2_SC_MODE_OR		(0 << 3)
#	define R128_AUX2_SC_MODE_NAND		(1 << 3)
#	define R128_AUX3_SC_EN			(1 << 4)
#	define R128_AUX3_SC_MODE_OR		(0 << 5)
#	define R128_AUX3_SC_MODE_NAND		(1 << 5)
#define R128_AUX1_SC_LEFT		0x1664
#define R128_AUX1_SC_RIGHT		0x1668
#define R128_AUX1_SC_TOP		0x166c
#define R128_AUX1_SC_BOTTOM		0x1670
#define R128_AUX2_SC_LEFT		0x1674
#define R128_AUX2_SC_RIGHT		0x1678
#define R128_AUX2_SC_TOP		0x167c
#define R128_AUX2_SC_BOTTOM		0x1680
#define R128_AUX3_SC_LEFT		0x1684
#define R128_AUX3_SC_RIGHT		0x1688
#define R128_AUX3_SC_TOP		0x168c
#define R128_AUX3_SC_BOTTOM		0x1690

#define R128_BRUSH_DATA0		0x1480
#define R128_BUS_CNTL			0x0030
#	define R128_BUS_MASTER_DIS		(1 << 6)

#define R128_CLOCK_CNTL_INDEX		0x0008
#define R128_CLOCK_CNTL_DATA		0x000c
#	define R128_PLL_WR_EN			(1 << 7)
#define R128_CONSTANT_COLOR_C		0x1d34
#define R128_CRTC_OFFSET		0x0224
#define R128_CRTC_OFFSET_CNTL		0x0228
#	define R128_CRTC_OFFSET_FLIP_CNTL	(1 << 16)

#define R128_DP_GUI_MASTER_CNTL		0x146c
#       define R128_GMC_SRC_PITCH_OFFSET_CNTL	(1    <<  0)
#       define R128_GMC_DST_PITCH_OFFSET_CNTL	(1    <<  1)
#	define R128_GMC_BRUSH_SOLID_COLOR	(13   <<  4)
#	define R128_GMC_BRUSH_NONE		(15   <<  4)
#	define R128_GMC_DST_16BPP		(4    <<  8)
#	define R128_GMC_DST_24BPP		(5    <<  8)
#	define R128_GMC_DST_32BPP		(6    <<  8)
#       define R128_GMC_DST_DATATYPE_SHIFT	8
#	define R128_GMC_SRC_DATATYPE_COLOR	(3    << 12)
#	define R128_DP_SRC_SOURCE_MEMORY	(2    << 24)
#	define R128_DP_SRC_SOURCE_HOST_DATA	(3    << 24)
#	define R128_GMC_CLR_CMP_CNTL_DIS	(1    << 28)
#	define R128_GMC_AUX_CLIP_DIS		(1    << 29)
#	define R128_GMC_WR_MSK_DIS		(1    << 30)
#	define R128_ROP3_S			0x00cc0000
#	define R128_ROP3_P			0x00f00000
#define R128_DP_WRITE_MASK		0x16cc
#define R128_DST_PITCH_OFFSET_C		0x1c80
#	define R128_DST_TILE			(1 << 31)

#define R128_GEN_INT_CNTL		0x0040
#	define R128_CRTC_VBLANK_INT_EN		(1 <<  0)
#define R128_GEN_INT_STATUS		0x0044
#	define R128_CRTC_VBLANK_INT		(1 <<  0)
#	define R128_CRTC_VBLANK_INT_AK		(1 <<  0)
#define R128_GEN_RESET_CNTL		0x00f0
#	define R128_SOFT_RESET_GUI		(1 <<  0)

#define R128_GUI_SCRATCH_REG0		0x15e0
#define R128_GUI_SCRATCH_REG1		0x15e4
#define R128_GUI_SCRATCH_REG2		0x15e8
#define R128_GUI_SCRATCH_REG3		0x15ec
#define R128_GUI_SCRATCH_REG4		0x15f0
#define R128_GUI_SCRATCH_REG5		0x15f4

#define R128_GUI_STAT			0x1740
#	define R128_GUI_FIFOCNT_MASK		0x0fff
#	define R128_GUI_ACTIVE			(1 << 31)

#define R128_MCLK_CNTL			0x000f
#	define R128_FORCE_GCP			(1 << 16)
#	define R128_FORCE_PIPE3D_CP		(1 << 17)
#	define R128_FORCE_RCP			(1 << 18)

#define R128_PC_GUI_CTLSTAT		0x1748
#define R128_PC_NGUI_CTLSTAT		0x0184
#	define R128_PC_FLUSH_GUI		(3 << 0)
#	define R128_PC_RI_GUI			(1 << 2)
#	define R128_PC_FLUSH_ALL		0x00ff
#	define R128_PC_BUSY			(1 << 31)

#define R128_PCI_GART_PAGE		0x017c
#define R128_PRIM_TEX_CNTL_C		0x1cb0

#define R128_SCALE_3D_CNTL		0x1a00
#define R128_SEC_TEX_CNTL_C		0x1d00
#define R128_SEC_TEXTURE_BORDER_COLOR_C	0x1d3c
#define R128_SETUP_CNTL			0x1bc4
#define R128_STEN_REF_MASK_C		0x1d40

#define R128_TEX_CNTL_C			0x1c9c
#	define R128_TEX_CACHE_FLUSH		(1 << 23)

#define R128_WAIT_UNTIL			0x1720
#	define R128_EVENT_CRTC_OFFSET		(1 << 0)
#define R128_WINDOW_XY_OFFSET		0x1bcc


/* CCE registers
 */
#define R128_PM4_BUFFER_OFFSET		0x0700
#define R128_PM4_BUFFER_CNTL		0x0704
#	define R128_PM4_MASK			(15 << 28)
#	define R128_PM4_NONPM4			(0  << 28)
#	define R128_PM4_192PIO			(1  << 28)
#	define R128_PM4_192BM			(2  << 28)
#	define R128_PM4_128PIO_64INDBM		(3  << 28)
#	define R128_PM4_128BM_64INDBM		(4  << 28)
#	define R128_PM4_64PIO_128INDBM		(5  << 28)
#	define R128_PM4_64BM_128INDBM		(6  << 28)
#	define R128_PM4_64PIO_64VCBM_64INDBM	(7  << 28)
#	define R128_PM4_64BM_64VCBM_64INDBM	(8  << 28)
#	define R128_PM4_64PIO_64VCPIO_64INDPIO	(15 << 28)

#define R128_PM4_BUFFER_WM_CNTL		0x0708
#	define R128_WMA_SHIFT			0
#	define R128_WMB_SHIFT			8
#	define R128_WMC_SHIFT			16
#	define R128_WB_WM_SHIFT			24

#define R128_PM4_BUFFER_DL_RPTR_ADDR	0x070c
#define R128_PM4_BUFFER_DL_RPTR		0x0710
#define R128_PM4_BUFFER_DL_WPTR		0x0714
#	define R128_PM4_BUFFER_DL_DONE		(1 << 31)

#define R128_PM4_VC_FPU_SETUP		0x071c

#define R128_PM4_IW_INDOFF		0x0738
#define R128_PM4_IW_INDSIZE		0x073c

#define R128_PM4_STAT			0x07b8
#	define R128_PM4_FIFOCNT_MASK		0x0fff
#	define R128_PM4_BUSY			(1 << 16)
#	define R128_PM4_GUI_ACTIVE		(1 << 31)

#define R128_PM4_MICROCODE_ADDR		0x07d4
#define R128_PM4_MICROCODE_RADDR	0x07d8
#define R128_PM4_MICROCODE_DATAH	0x07dc
#define R128_PM4_MICROCODE_DATAL	0x07e0

#define R128_PM4_BUFFER_ADDR		0x07f0
#define R128_PM4_MICRO_CNTL		0x07fc
#	define R128_PM4_MICRO_FREERUN		(1 << 30)

#define R128_PM4_FIFO_DATA_EVEN		0x1000
#define R128_PM4_FIFO_DATA_ODD		0x1004


/* CCE command packets
 */
#define R128_CCE_PACKET0		0x00000000
#define R128_CCE_PACKET1		0x40000000
#define R128_CCE_PACKET2		0x80000000
#define R128_CCE_PACKET3		0xC0000000
#	define R128_CNTL_HOSTDATA_BLT		0x00009400
#	define R128_CNTL_PAINT_MULTI		0x00009A00
#	define R128_CNTL_BITBLT_MULTI		0x00009B00
#	define R128_3D_RNDR_GEN_INDX_PRIM	0x00002300

#define R128_CCE_PACKET_MASK		0xC0000000
#define R128_CCE_PACKET_COUNT_MASK	0x3fff0000
#define R128_CCE_PACKET0_REG_MASK	0x000007ff
#define R128_CCE_PACKET1_REG0_MASK	0x000007ff
#define R128_CCE_PACKET1_REG1_MASK	0x003ff800

#define R128_CCE_VC_CNTL_PRIM_TYPE_NONE		0x00000000
#define R128_CCE_VC_CNTL_PRIM_TYPE_POINT	0x00000001
#define R128_CCE_VC_CNTL_PRIM_TYPE_LINE		0x00000002
#define R128_CCE_VC_CNTL_PRIM_TYPE_POLY_LINE	0x00000003
#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_LIST	0x00000004
#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_FAN	0x00000005
#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_STRIP	0x00000006
#define R128_CCE_VC_CNTL_PRIM_TYPE_TRI_TYPE2	0x00000007
#define R128_CCE_VC_CNTL_PRIM_WALK_IND		0x00000010
#define R128_CCE_VC_CNTL_PRIM_WALK_LIST		0x00000020
#define R128_CCE_VC_CNTL_PRIM_WALK_RING		0x00000030
#define R128_CCE_VC_CNTL_NUM_SHIFT		16

#define R128_DATATYPE_VQ		0
#define R128_DATATYPE_CI4		1
#define R128_DATATYPE_CI8		2
#define R128_DATATYPE_ARGB1555		3
#define R128_DATATYPE_RGB565		4
#define R128_DATATYPE_RGB888		5
#define R128_DATATYPE_ARGB8888		6
#define R128_DATATYPE_RGB332		7
#define R128_DATATYPE_Y8		8
#define R128_DATATYPE_RGB8		9
#define R128_DATATYPE_CI16		10
#define R128_DATATYPE_YVYU422		11
#define R128_DATATYPE_VYUY422		12
#define R128_DATATYPE_AYUV444		14
#define R128_DATATYPE_ARGB4444		15

/* Constants */
#define R128_AGP_OFFSET			0x02000000

#define R128_WATERMARK_L		16
#define R128_WATERMARK_M		8
#define R128_WATERMARK_N		8
#define R128_WATERMARK_K		128

#define R128_MAX_USEC_TIMEOUT		100000	/* 100 ms */

#define R128_LAST_FRAME_REG		R128_GUI_SCRATCH_REG0
#define R128_LAST_DISPATCH_REG		R128_GUI_SCRATCH_REG1
#define R128_MAX_VB_AGE			0x7fffffff
#define R128_MAX_VB_VERTS		(0xffff)

#define R128_RING_HIGH_MARK		128

#define R128_PERFORMANCE_BOXES		0

#define R128_READ(reg)		DRM_READ32(  dev_priv->mmio, (reg) )
#define R128_WRITE(reg,val)	DRM_WRITE32( dev_priv->mmio, (reg), (val) )
#define R128_READ8(reg)		DRM_READ8(   dev_priv->mmio, (reg) )
#define R128_WRITE8(reg,val)	DRM_WRITE8(  dev_priv->mmio, (reg), (val) )

#define R128_WRITE_PLL(addr,val)					\
do {									\
	R128_WRITE8(R128_CLOCK_CNTL_INDEX,				\
		    ((addr) & 0x1f) | R128_PLL_WR_EN);			\
	R128_WRITE(R128_CLOCK_CNTL_DATA, (val));			\
} while (0)

extern int R128_READ_PLL(drm_device_t *dev, int addr);


#define CCE_PACKET0( reg, n )		(R128_CCE_PACKET0 |		\
					 ((n) << 16) | ((reg) >> 2))
#define CCE_PACKET1( reg0, reg1 )	(R128_CCE_PACKET1 |		\
					 (((reg1) >> 2) << 11) | ((reg0) >> 2))
#define CCE_PACKET2()			(R128_CCE_PACKET2)
#define CCE_PACKET3( pkt, n )		(R128_CCE_PACKET3 |		\
					 (pkt) | ((n) << 16))


/* ================================================================