summaryrefslogtreecommitdiff
path: root/linux-core/i810_drm.h
blob: d803aeca93944f67aacaf3f216c42e04f43d331e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
#ifndef _I810_DRM_H_
#define _I810_DRM_H_

/* WARNING: These defines must be the same as what the Xserver uses.
 * if you change them, you must change the defines in the Xserver.
 */

#ifndef _I810_DEFINES_
#define _I810_DEFINES_

#define I810_DMA_BUF_ORDER		12
#define I810_DMA_BUF_SZ 		(1<<I810_DMA_BUF_ORDER)
#define I810_DMA_BUF_NR 		256
#define I810_NR_SAREA_CLIPRECTS 	8

/* Each region is a minimum of 64k, and there are at most 64 of them.
 */
#define I810_NR_TEX_REGIONS 64
#define I810_LOG_MIN_TEX_REGION_SIZE 16
#endif

#define I810_UPLOAD_TEX0IMAGE  0x1	/* handled clientside */
#define I810_UPLOAD_TEX1IMAGE  0x2	/* handled clientside */
#define I810_UPLOAD_CTX        0x4
#define I810_UPLOAD_BUFFERS    0x8
#define I810_UPLOAD_TEX0       0x10
#define I810_UPLOAD_TEX1       0x20
#define I810_UPLOAD_CLIPRECTS  0x40

/* Indices into buf.Setup where various bits of state are mirrored per
 * context and per buffer.  These can be fired at the card as a unit,
 * or in a piecewise fashion as required.
 */

/* Destbuffer state
 *    - backbuffer linear offset and pitch -- invarient in the current dri
 *    - zbuffer linear offset and pitch -- also invarient
 *    - drawing origin in back and depth buffers.
 *
 * Keep the depth/back buffer state here to accommodate private buffers
 * in the future.
 */
#define I810_DESTREG_DI0  0	/* CMD_OP_DESTBUFFER_INFO (2 dwords) */
#define I810_DESTREG_DI1  1
#define I810_DESTREG_DV0  2	/* GFX_OP_DESTBUFFER_VARS (2 dwords) */
#define I810_DESTREG_DV1  3
#define I810_DESTREG_DR0  4	/* GFX_OP_DRAWRECT_INFO (4 dwords) */
#define I810_DESTREG_DR1  5
#define I810_DESTREG_DR2  6
#define I810_DESTREG_DR3  7
#define I810_DESTREG_DR4  8
#define I810_DEST_SETUP_SIZE 10

/* Context state
 */
#define I810_CTXREG_CF0   0	/* GFX_OP_COLOR_FACTOR */
#define I810_CTXREG_CF1   1
#define I810_CTXREG_ST0   2	/* GFX_OP_STIPPLE */
#define I810_CTXREG_ST1   3
#define I810_CTXREG_VF    4	/* GFX_OP_VERTEX_FMT */
#define I810_CTXREG_MT    5	/* GFX_OP_MAP_TEXELS */
#define I810_CTXREG_MC0   6	/* GFX_OP_MAP_COLOR_STAGES - stage 0 */
#define I810_CTXREG_MC1   7	/* GFX_OP_MAP_COLOR_STAGES - stage 1 */
#define I810_CTXREG_MC2   8	/* GFX_OP_MAP_COLOR_STAGES - stage 2 */
#define I810_CTXREG_MA0   9	/* GFX_OP_MAP_ALPHA_STAGES - stage 0 */
#define I810_CTXREG_MA1   10	/* GFX_OP_MAP_ALPHA_STAGES - stage 1 */
#define I810_CTXREG_MA2   11	/* GFX_OP_MAP_ALPHA_STAGES - stage 2 */
#define I810_CTXREG_SDM   12	/* GFX_OP_SRC_DEST_MONO */
#define I810_CTXREG_FOG   13	/* GFX_OP_FOG_COLOR */
#define I810_CTXREG_B1    14	/* GFX_OP_BOOL_1 */
#define I810_CTXREG_B2    15	/* GFX_OP_BOOL_2 */
#define I810_CTXREG_LCS   16	/* GFX_OP_LINEWIDTH_CULL_SHADE_MODE */
#define I810_CTXREG_PV    17	/* GFX_OP_PV_RULE -- Invarient! */
#define I810_CTXREG_ZA    18	/* GFX_OP_ZBIAS_ALPHAFUNC */
#define I810_CTXREG_AA    19	/* GFX_OP_ANTIALIAS */
#define I810_CTX_SETUP_SIZE 20

/* Texture state (per tex unit)
 */
#define I810_TEXREG_MI0  0	/* GFX_OP_MAP_INFO (4 dwords) */
#define I810_TEXREG_MI1  1
#define I810_TEXREG_MI2  2
#define I810_TEXREG_MI3  3
#define I810_TEXREG_MF   4	/* GFX_OP_MAP_FILTER */
#define I810_TEXREG_MLC  5	/* GFX_OP_MAP_LOD_CTL */
#define I810_TEXREG_MLL  6	/* GFX_OP_MAP_LOD_LIMITS */
#define I810_TEXREG_MCS  7	/* GFX_OP_MAP_COORD_SETS ??? */
#define I810_TEX_SETUP_SIZE 8

/* Flags for clear ioctl
 */
#define I810_FRONT   0x1
#define I810_BACK    0x2
#define I810_DEPTH   0x4

typedef enum _drm_i810_init_func {
	I810_INIT_DMA = 0x01,
	I810_CLEANUP_DMA = 0x02,
	I810_INIT_DMA_1_4 = 0x03
} drm_i810_init_func_t;

/* This is the init structure after v1.2 */
typedef struct _drm_i810_init {
	drm_i810_init_func_t func;
	unsigned int mmio_offset;
	unsigned int buffers_offset;
	int sarea_priv_offset;
	unsigned int ring_start;
	unsigned int ring_end;
	unsigned int ring_size;
	unsigned int front_offset;
	unsigned int back_offset;
	unsigned int depth_offset;
	unsigned int overlay_offset;
	unsigned int overlay_physical;
	unsigned int w;
	unsigned int h;
	unsigned int pitch;
	unsigned int pitch_bits;
} drm_i810_init_t;

/* Warning: If you change the SAREA structure you must change the Xserver
 * structure as well */

typedef struct _drm_i810_tex_region {
	unsigned char next, prev;	/* indices to form a circular LRU  */
	unsigned char in_use;	/* owned by a client, or free? */
	int age;		/* tracked by clients to update local LRU's */
} drm_i810_tex_region_t;

typedef struct _drm_i810_sarea {
	unsigned int ContextState[I810_CTX_SETUP_SIZE];
	unsigned int BufferState[I810_DEST_SETUP_SIZE];
	unsigned int TexState[2][I810_TEX_SETUP_SIZE];
	unsigned int dirty;

	unsigned int nbox;
	struct drm_clip_rect boxes[I810_NR_SAREA_CLIPRECTS];

	/* Maintain an LRU of contiguous regions of texture space.  If
	 * you think you own a region of texture memory, and it has an
	 * age different to the one you set, then you are mistaken and
	 * it has been stolen by another client.  If global texAge
	 * hasn't changed, there is no need to walk the list.
	 *
	 * These regions can be used as a proxy for the fine-grained
	 * texture information of other clients - by maintaining them
	 * in the same lru which is used to age their own textures,
	 * clients have an approximate lru for the whole of global
	 * texture space, and can make informed decisions as to which
	 * areas to kick out.  There is no need to choose whether to
	 * kick out your own texture or someone else's - simply eject
	 * them all in LRU order.
	 */

	drm_i810_tex_region_t texList[I810_NR_TEX_REGIONS + 1];
	/* Last elt is sentinal */
	int texAge;		/* last time texture was uploaded */
	int last_enqueue;	/* last time a buffer was enqueued */
	int last_dispatch;	/* age of the most recently dispatched buffer */
	int last_quiescent;	/*  */
	int ctxOwner;		/* last context to upload state */

	int vertex_prim;

	int pf_enabled;		/* is pageflipping allowed? */
	int pf_active;
	int pf_current_page;	/* which buffer is being displayed? */
} drm_i810_sarea_t;

/* WARNING: If you change any of these defines, make sure to change the
 * defines in the Xserver file (xf86drmMga.h)
 */

/* i810 specific ioctls
 * The device specific ioctl range is 0x40 to 0x79.
 */
#define DRM_I810_INIT		0x00
#define DRM_I810_VERTEX		0x01
#define DRM_I810_CLEAR		0x02
#define DRM_I810_FLUSH		0x03
#define DRM_I810_GETAGE		0x04
#define DRM_I810_GETBUF		0x05
#define DRM_I810_SWAP		0x06
#define DRM_I810_COPY		0x07
#define DRM_I810_DOCOPY		0x08
#define DRM_I810_OV0INFO	0x09
#define DRM_I810_FSTATUS	0x0a
#define DRM_I810_OV0FLIP	0x0b
#define DRM_I810_MC		0x0c
#define DRM_I810_RSTATUS	0x0d
#define DRM_I810_FLIP		0x0e

#define DRM_IOCTL_I810_INIT		DRM_IOW( DRM_COMMAND_BASE + DRM_I810_INIT, drm_i810_init_t)
#define DRM_IOCTL_I810_VERTEX		DRM_IOW( DRM_COMMAND_BASE + DRM_I810_VERTEX, drm_i810_vertex_t)
#define DRM_IOCTL_I810_CLEAR		DRM_IOW( DRM_COMMAND_BASE + DRM_I810_CLEAR, drm_i810_clear_t)
#define DRM_IOCTL_I810_FLUSH		DRM_IO(  DRM_COMMAND_BASE + DRM_I810_FLUSH)
#define DRM_IOCTL_I810_GETAGE		DRM_IO(  DRM_COMMAND_BASE + DRM_I810_GETAGE)
#define DRM_IOCTL_I810_GETBUF		DRM_IOWR(DRM_COMMAND_BASE + DRM_I810_GETBUF, drm_i810_dma_t)
#define DRM_IOCTL_I810_SWAP		DRM_IO(  DRM_COMMAND_BASE + DRM_I810_SWAP)
#define DRM_IOCTL_I810_COPY		DRM_IOW( DRM_COMMAND_BASE + DRM_I810_COPY, drm_i810_copy_t)
#define DRM_IOCTL_I810_DOCOPY		DRM_IO(  DRM_COMMAND_BASE + DRM_I810_DOCOPY)
#define DRM_IOCTL_I810_OV0INFO		DRM_IOR( DRM_COMMAND_BASE + DRM_I810_OV0INFO, drm_i810_overlay_t)
#define DRM_IOCTL_I810_FSTATUS		DRM_IO ( DRM_COMMAND_BASE + DRM_I810_FSTATUS)
#define DRM_IOCTL_I810_OV0FLIP		DRM_IO ( DRM_COMMAND_BASE + DRM_I810_OV0FLIP)
#define DRM_IOCTL_I810_MC		DRM_IOW( DRM_COMMAND_BASE + DRM_I810_MC, drm_i810_mc_t)
#define DRM_IOCTL_I810_RSTATUS		DRM_IO ( DRM_COMMAND_BASE + DRM_I810_RSTATUS)
#define DRM_IOCTL_I810_FLIP             DRM_IO ( DRM_COMMAND_BASE + DRM_I810_FLIP)

typedef struct _drm_i810_clear {
	int clear_color;
	int clear_depth;
	int flags;
} drm_i810_clear_t;

/* These may be placeholders if we have more cliprects than
 * I810_NR_SAREA_CLIPRECTS.  In that case, the client sets discard to
 * false, indicating that the buffer will be dispatched again with a
 * new set of cliprects.
 */
typedef struct _drm_i810_vertex {
	int idx;		/* buffer index */
	int used;		/* nr bytes in use */
	int discard;		/* client is finished with the buffer? */
} drm_i810_vertex_t;

typedef struct _drm_i810_copy_t {
	int idx;		/* buffer index */
	int used;		/* nr bytes in use */
	void *address;		/* Address to copy from */
} drm_i810_copy_t;

#define PR_TRIANGLES         (0x0<<18)
#define PR_TRISTRIP_0        (0x1<<18)
#define PR_TRISTRIP_1        (0x2<<18)
#define PR_TRIFAN            (0x3<<18)
#define PR_POLYGON           (0x4<<18)
#define PR_LINES             (0x5<<18)
#define PR_LINESTRIP         (0x6<<18)
#define PR_RECTS             (0x7<<18)
#define PR_MASK              (0x7<<18)

typedef struct drm_i810_dma {
	void *virtual;
	int request_idx;
	int request_size;
	int granted;
} drm_i810_dma_t;

typedef struct _drm_i810_overlay_t {
	unsigned int offset;	/* Address of the Overlay Regs */
	unsigned int physical;
} drm_i810_overlay_t;

typedef struct _drm_i810_mc {
	int idx;		/* buffer index */
	int used;		/* nr bytes in use */
	int num_blocks;		/* number of GFXBlocks */
	int *length;		/* List of lengths for GFXBlocks (FUTURE) */
	unsigned int last_render;	/* Last Render Request */
} drm_i810_mc_t;

#endif				/* _I810_DRM_H_ */
ies 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 THE COPYRIGHT OWNER(S) 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. * */ #include <asm/byteorder.h> #include "nouveau_bios.h" #include "nouveau_drv.h" /* returns true if it mismatches */ static bool nv_checksum(const uint8_t *data, unsigned int length) { /* there's a few checksums in the BIOS, so here's a generic checking function */ int i; uint8_t sum = 0; for (i = 0; i < length; i++) sum += data[i]; if (sum) return true; return false; } static int nv_valid_bios(struct drm_device *dev, uint8_t *data) { /* check for BIOS signature */ if (!(data[0] == 0x55 && data[1] == 0xAA)) { DRM_ERROR("BIOS signature not found.\n"); return 0; } if (nv_checksum(data, data[2] * 512)) { DRM_ERROR("BIOS checksum invalid.\n"); return 1; } return 2; } static void nv_shadow_bios_rom(struct drm_device *dev, uint8_t *data) { struct drm_nouveau_private *dev_priv = dev->dev_private; int i; /* enable access to rom */ NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED); /* This is also valid for pre-NV50, it just happened to be the only define already present. */ for (i=0; i < NV50_PROM__ESIZE; i++) { /* Appearantly needed for a 6600GT/6800LE bug. */ data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); data[i] = DRM_READ8(dev_priv->mmio, NV50_PROM + i); } /* disable access to rom */ NV_WRITE(NV04_PBUS_PCI_NV_20, NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED); } static void nv_shadow_bios_ramin(struct drm_device *dev, uint8_t *data) { struct drm_nouveau_private *dev_priv = dev->dev_private; uint32_t old_bar0_pramin = 0; int i; /* Move the bios copy to the start of ramin? */ if (dev_priv->card_type >= NV_50) { uint32_t vbios_vram = (NV_READ(0x619f04) & ~0xff) << 8; if (!vbios_vram) vbios_vram = (NV_READ(0x1700) << 16) + 0xf0000; old_bar0_pramin = NV_READ(0x1700); NV_WRITE(0x1700, vbios_vram >> 16); } for (i=0; i < NV50_PROM__ESIZE; i++) data[i] = DRM_READ8(dev_priv->mmio, NV04_PRAMIN + i); if (dev_priv->card_type >= NV_50) NV_WRITE(0x1700, old_bar0_pramin); } static bool nv_shadow_bios(struct drm_device *dev, uint8_t *data) { nv_shadow_bios_rom(dev, data); if (nv_valid_bios(dev, data) == 2) return true; nv_shadow_bios_ramin(dev, data); if (nv_valid_bios(dev, data)) return true; return false; } struct bit_entry { uint8_t id[2]; uint16_t length; uint16_t offset; }; static int parse_bit_A_tbl_entry(struct drm_device *dev, struct bios *bios, struct bit_entry *bitentry) { /* Parses the load detect value table. * * Starting at bitentry->offset: * * offset + 0 (16 bits): table pointer */ uint16_t load_table_pointer; if (bitentry->length != 3) { DRM_ERROR("Do not understand BIT loadval table\n"); return 0; } load_table_pointer = le16_to_cpu(*((uint16_t *)(&bios->data[bitentry->offset]))); if (load_table_pointer == 0x0) { DRM_ERROR("Pointer to loadval table invalid\n"); return 0; } /* Some kind of signature */ if (bios->data[load_table_pointer] != 16 || bios->data[load_table_pointer + 1] != 4 || bios->data[load_table_pointer + 2] != 4 || bios->data[load_table_pointer + 3] != 2) return 0; bios->dactestval = le32_to_cpu(*((uint32_t *)&bios->data[load_table_pointer + 4])) & 0x3FF; return 1; } static int parse_bit_C_tbl_entry(struct drm_device *dev, struct bios *bios, struct bit_entry *bitentry) { /* offset + 8 (16 bits): PLL limits table pointer * * There's more in here, but that's unknown. */ if (bitentry->length < 10) { DRM_ERROR("Do not understand BIT C table\n"); return 0; } bios->pll_limit_tbl_ptr = le16_to_cpu(*((uint16_t *)(&bios->data[bitentry->offset + 8]))); return 1; } static void parse_bit_structure(struct drm_device *dev, struct bios *bios, const uint16_t bitoffset) { int entries = bios->data[bitoffset + 4]; /* parse i first, I next (which needs C & M before it), and L before D */ char parseorder[] = "iCMILDTA"; struct bit_entry bitentry; int i, j, offset; for (i = 0; i < sizeof(parseorder); i++) { for (j = 0, offset = bitoffset + 6; j < entries; j++, offset += 6) { bitentry.id[0] = bios->data[offset]; bitentry.id[1] = bios->data[offset + 1]; bitentry.length = le16_to_cpu(*((uint16_t *)&bios->data[offset + 2])); bitentry.offset = le16_to_cpu(*((uint16_t *)&bios->data[offset + 4])); if (bitentry.id[0] != parseorder[i]) continue; switch (bitentry.id[0]) { case 'A': parse_bit_A_tbl_entry(dev, bios, &bitentry); break; case 'C': parse_bit_C_tbl_entry(dev, bios, &bitentry); break; } } } } static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len) { int i, j; for (i = 0; i <= (n - len); i++) { for (j = 0; j < len; j++) if (data[i + j] != str[j]) break; if (j == len) return i; } return 0; } static void read_dcb_i2c_entry(struct drm_device *dev, uint8_t dcb_version, uint16_t i2ctabptr, int index) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct bios *bios = &dev_priv->bios; uint8_t *i2ctable = &bios->data[i2ctabptr]; uint8_t headerlen = 0; int i2c_entries = MAX_NUM_DCB_ENTRIES; int recordoffset = 0, rdofs = 1, wrofs = 0; if (!i2ctabptr) return; if (dcb_version >= 0x30) { if (i2ctable[0] != dcb_version) /* necessary? */ DRM_ERROR( "DCB I2C table version mismatch (%02X vs %02X)\n", i2ctable[0], dcb_version); headerlen = i2ctable[1]; i2c_entries = i2ctable[2]; /* same address offset used for read and write for C51 and G80 */ if (bios->chip_version == 0x51) rdofs = wrofs = 1; if (i2ctable[0] >= 0x40) rdofs = wrofs = 0; } /* it's your own fault if you call this function on a DCB 1.1 BIOS -- * the test below is for DCB 1.2 */ if (dcb_version < 0x14) { recordoffset = 2; rdofs = 0; wrofs = 1; } if (index == 0xf) return; if (index > i2c_entries) { DRM_ERROR( "DCB I2C index too big (%d > %d)\n", index, i2ctable[2]); return; } if (i2ctable[headerlen + 4 * index + 3] == 0xff) { DRM_ERROR( "DCB I2C entry invalid\n"); return; } if (bios->chip_version == 0x51) { int port_type = i2ctable[headerlen + 4 * index + 3]; if (port_type != 4) DRM_ERROR( "DCB I2C table has port type %d\n", port_type); } if (i2ctable[0] >= 0x40) { int port_type = i2ctable[headerlen + 4 * index + 3]; if (port_type != 5) DRM_ERROR( "DCB I2C table has port type %d\n", port_type); } dev_priv->dcb_table.i2c_read[index] = i2ctable[headerlen + recordoffset + rdofs + 4 * index]; dev_priv->dcb_table.i2c_write[index] = i2ctable[headerlen + recordoffset + wrofs + 4 * index]; } static bool parse_dcb_entry(struct drm_device *dev, int index, uint8_t dcb_version, uint16_t i2ctabptr, uint32_t conn, uint32_t conf) { struct drm_nouveau_private *dev_priv = dev->dev_private; struct dcb_entry *entry = &dev_priv->dcb_table.entry[index]; memset(entry, 0, sizeof (struct dcb_entry)); entry->index = index; /* safe defaults for a crt */ entry->type = 0; entry->i2c_index = 0; entry->heads = 1; entry->bus = 0; entry->location = LOC_ON_CHIP; entry->or = 1; entry->duallink_possible = false; if (dcb_version >= 0x20) { entry->type = conn & 0xf; entry->i2c_index = (conn >> 4) & 0xf; entry->heads = (conn >> 8) & 0xf; entry->bus = (conn >> 16) & 0xf; entry->location = (conn >> 20) & 0xf; entry->or = (conn >> 24) & 0xf; /* Normal entries consist of a single bit, but dual link has the * adjacent more significant bit set too */ if ((1 << (ffs(entry->or) - 1)) * 3 == entry->or) entry->duallink_possible = true; switch (entry->type) { case DCB_OUTPUT_LVDS: { uint32_t mask; if (conf & 0x1) entry->lvdsconf.use_straps_for_mode = true; if (dcb_version < 0x22) { mask = ~0xd; /* both 0x4 and 0x8 show up in v2.0 tables; assume they mean * the same thing, which is probably wrong, but might work */ if (conf & 0x4 || conf & 0x8) entry->lvdsconf.use_power_scripts = true; } else { mask = ~0x5; if (conf & 0x4) entry->lvdsconf.use_power_scripts = true; } if (conf & mask) { if (dcb_version < 0x40) { /* we know g80 cards have unknown bits */ DRM_ERROR("Unknown LVDS configuration bits, please report\n"); /* cause output setting to fail, so message is seen */ dev_priv->dcb_table.entries = 0; return false; } } break; } case 0xe: /* weird type that appears on g80 mobile bios; nv driver treats it as a terminator */ return false; } read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index); } else if (dcb_version >= 0x14 ) { if (conn != 0xf0003f00 && conn != 0xf2247f10 && conn != 0xf2204001 && conn != 0xf2204301 && conn != 0xf2244311 && conn != 0xf2045f14 && conn != 0xf2205004 && conn != 0xf2208001 && conn != 0xf4204011 && conn != 0xf4208011 && conn != 0xf4248011) { DRM_ERROR( "Unknown DCB 1.4 / 1.5 entry, please report\n"); /* cause output setting to fail, so message is seen */ dev_priv->dcb_table.entries = 0; return false; } /* most of the below is a "best guess" atm */ entry->type = conn & 0xf; if (entry->type == 4) { /* digital */ if (conn & 0x10) entry->type = DCB_OUTPUT_LVDS; else entry->type = DCB_OUTPUT_TMDS; } /* what's in bits 5-13? could be some brooktree/chrontel/philips thing, in tv case */ entry->i2c_index = (conn >> 14) & 0xf; /* raw heads field is in range 0-1, so move to 1-2 */ entry->heads = ((conn >> 18) & 0x7) + 1; entry->location = (conn >> 21) & 0xf; entry->bus = (conn >> 25) & 0x7; /* set or to be same as heads -- hopefully safe enough */ entry->or = entry->heads; switch (entry->type) { case DCB_OUTPUT_LVDS: /* this is probably buried in conn's unknown bits */ entry->lvdsconf.use_power_scripts = true; break; case DCB_OUTPUT_TMDS: /* invent a DVI-A output, by copying the fields of the DVI-D output * reported to work by math_b on an NV20(!) */ memcpy(&entry[1], &entry[0], sizeof(struct dcb_entry)); entry[1].type = DCB_OUTPUT_ANALOG; dev_priv->dcb_table.entries++; } read_dcb_i2c_entry(dev, dcb_version, i2ctabptr, entry->i2c_index); } else if (dcb_version >= 0x12) { /* v1.2 tables normally have the same 5 entries, which are not * specific to the card, so use the defaults for a crt */ /* DCB v1.2 does have an I2C table that read_dcb_i2c_table can handle, but cards * exist (seen on nv11) where the pointer to the table points to the wrong * place, so for now, we rely on the indices parsed in parse_bmp_structure */ entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt; } else { /* pre DCB / v1.1 - use the safe defaults for a crt */ DRM_ERROR( "No information in BIOS output table; assuming a CRT output exists\n"); entry->i2c_index = dev_priv->bios.legacy.i2c_indices.crt; } if (entry->type == DCB_OUTPUT_LVDS && dev_priv->bios.fp.strapping != 0xff) entry->lvdsconf.use_straps_for_mode = true; dev_priv->dcb_table.entries++; return true; } static void merge_like_dcb_entries(struct drm_device *dev) { struct drm_nouveau_private *dev_priv = dev->dev_private; /* DCB v2.0 lists each output combination separately. * Here we merge compatible entries to have fewer outputs, with more options */ int i, newentries = 0; for (i = 0; i < dev_priv->dcb_table.entries; i++) { struct dcb_entry *ient = &dev_priv->dcb_table.entry[i]; int j;