Home | History | Annotate | Line # | Download | only in dispnv50
      1 /*	$NetBSD: nouveau_dispnv50_head507d.c,v 1.4 2021/12/19 10:49:47 riastradh Exp $	*/
      2 
      3 /*
      4  * Copyright 2018 Red Hat Inc.
      5  *
      6  * Permission is hereby granted, free of charge, to any person obtaining a
      7  * copy of this software and associated documentation files (the "Software"),
      8  * to deal in the Software without restriction, including without limitation
      9  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
     10  * and/or sell copies of the Software, and to permit persons to whom the
     11  * Software is furnished to do so, subject to the following conditions:
     12  *
     13  * The above copyright notice and this permission notice shall be included in
     14  * all copies or substantial portions of the Software.
     15  *
     16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
     19  * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
     20  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
     21  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
     22  * OTHER DEALINGS IN THE SOFTWARE.
     23  */
     24 #include <sys/cdefs.h>
     25 __KERNEL_RCSID(0, "$NetBSD: nouveau_dispnv50_head507d.c,v 1.4 2021/12/19 10:49:47 riastradh Exp $");
     26 
     27 #include "head.h"
     28 #include "core.h"
     29 
     30 #include <linux/nbsd-namespace.h>
     31 
     32 void
     33 head507d_procamp(struct nv50_head *head, struct nv50_head_atom *asyh)
     34 {
     35 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
     36 	u32 *push;
     37 	if ((push = evo_wait(core, 2))) {
     38 		evo_mthd(push, 0x08a8 + (head->base.index * 0x400), 1);
     39 		evo_data(push, asyh->procamp.sat.sin << 20 |
     40 			       asyh->procamp.sat.cos << 8);
     41 		evo_kick(push, core);
     42 	}
     43 }
     44 
     45 void
     46 head507d_dither(struct nv50_head *head, struct nv50_head_atom *asyh)
     47 {
     48 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
     49 	u32 *push;
     50 	if ((push = evo_wait(core, 2))) {
     51 		evo_mthd(push, 0x08a0 + (head->base.index * 0x0400), 1);
     52 		evo_data(push, asyh->dither.mode << 3 |
     53 			       asyh->dither.bits << 1 |
     54 			       asyh->dither.enable);
     55 		evo_kick(push, core);
     56 	}
     57 }
     58 
     59 void
     60 head507d_ovly(struct nv50_head *head, struct nv50_head_atom *asyh)
     61 {
     62 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
     63 	u32 bounds = 0;
     64 	u32 *push;
     65 
     66 	if (asyh->ovly.cpp) {
     67 		switch (asyh->ovly.cpp) {
     68 		case 4: bounds |= 0x00000300; break;
     69 		case 2: bounds |= 0x00000100; break;
     70 		default:
     71 			WARN_ON(1);
     72 			break;
     73 		}
     74 		bounds |= 0x00000001;
     75 	} else {
     76 		bounds |= 0x00000100;
     77 	}
     78 
     79 	if ((push = evo_wait(core, 2))) {
     80 		evo_mthd(push, 0x0904 + head->base.index * 0x400, 1);
     81 		evo_data(push, bounds);
     82 		evo_kick(push, core);
     83 	}
     84 }
     85 
     86 void
     87 head507d_base(struct nv50_head *head, struct nv50_head_atom *asyh)
     88 {
     89 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
     90 	u32 bounds = 0;
     91 	u32 *push;
     92 
     93 	if (asyh->base.cpp) {
     94 		switch (asyh->base.cpp) {
     95 		case 8: bounds |= 0x00000500; break;
     96 		case 4: bounds |= 0x00000300; break;
     97 		case 2: bounds |= 0x00000100; break;
     98 		case 1: bounds |= 0x00000000; break;
     99 		default:
    100 			WARN_ON(1);
    101 			break;
    102 		}
    103 		bounds |= 0x00000001;
    104 	}
    105 
    106 	if ((push = evo_wait(core, 2))) {
    107 		evo_mthd(push, 0x0900 + head->base.index * 0x400, 1);
    108 		evo_data(push, bounds);
    109 		evo_kick(push, core);
    110 	}
    111 }
    112 
    113 static void
    114 head507d_curs_clr(struct nv50_head *head)
    115 {
    116 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
    117 	u32 *push;
    118 	if ((push = evo_wait(core, 2))) {
    119 		evo_mthd(push, 0x0880 + head->base.index * 0x400, 1);
    120 		evo_data(push, 0x05000000);
    121 		evo_kick(push, core);
    122 	}
    123 }
    124 
    125 static void
    126 head507d_curs_set(struct nv50_head *head, struct nv50_head_atom *asyh)
    127 {
    128 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
    129 	u32 *push;
    130 	if ((push = evo_wait(core, 3))) {
    131 		evo_mthd(push, 0x0880 + head->base.index * 0x400, 2);
    132 		evo_data(push, 0x80000000 | asyh->curs.layout << 26 |
    133 					    asyh->curs.format << 24);
    134 		evo_data(push, asyh->curs.offset >> 8);
    135 		evo_kick(push, core);
    136 	}
    137 }
    138 
    139 int
    140 head507d_curs_format(struct nv50_head *head, struct nv50_wndw_atom *asyw,
    141 		     struct nv50_head_atom *asyh)
    142 {
    143 	switch (asyw->image.format) {
    144 	case 0xcf: asyh->curs.format = 1; break;
    145 	default:
    146 		WARN_ON(1);
    147 		return -EINVAL;
    148 	}
    149 	return 0;
    150 }
    151 
    152 int
    153 head507d_curs_layout(struct nv50_head *head, struct nv50_wndw_atom *asyw,
    154 		     struct nv50_head_atom *asyh)
    155 {
    156 	switch (asyw->image.w) {
    157 	case 32: asyh->curs.layout = 0; break;
    158 	case 64: asyh->curs.layout = 1; break;
    159 	default:
    160 		return -EINVAL;
    161 	}
    162 	return 0;
    163 }
    164 
    165 void
    166 head507d_core_clr(struct nv50_head *head)
    167 {
    168 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
    169 	u32 *push;
    170 	if ((push = evo_wait(core, 2))) {
    171 		evo_mthd(push, 0x0874 + head->base.index * 0x400, 1);
    172 		evo_data(push, 0x00000000);
    173 		evo_kick(push, core);
    174 	}
    175 }
    176 
    177 static void
    178 head507d_core_set(struct nv50_head *head, struct nv50_head_atom *asyh)
    179 {
    180 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
    181 	u32 *push;
    182 	if ((push = evo_wait(core, 9))) {
    183 		evo_mthd(push, 0x0860 + head->base.index * 0x400, 1);
    184 		evo_data(push, asyh->core.offset >> 8);
    185 		evo_mthd(push, 0x0868 + head->base.index * 0x400, 4);
    186 		evo_data(push, asyh->core.h << 16 | asyh->core.w);
    187 		evo_data(push, asyh->core.layout << 20 |
    188 			       (asyh->core.pitch >> 8) << 8 |
    189 			       asyh->core.blocks << 8 |
    190 			       asyh->core.blockh);
    191 		evo_data(push, asyh->core.kind << 16 |
    192 			       asyh->core.format << 8);
    193 		evo_data(push, asyh->core.handle);
    194 		evo_mthd(push, 0x08c0 + head->base.index * 0x400, 1);
    195 		evo_data(push, asyh->core.y << 16 | asyh->core.x);
    196 		evo_kick(push, core);
    197 
    198 		/* EVO will complain with INVALID_STATE if we have an
    199 		 * active cursor and (re)specify HeadSetContextDmaIso
    200 		 * without also updating HeadSetOffsetCursor.
    201 		 */
    202 		asyh->set.curs = asyh->curs.visible;
    203 		asyh->set.olut = asyh->olut.handle != 0;
    204 	}
    205 }
    206 
    207 void
    208 head507d_core_calc(struct nv50_head *head, struct nv50_head_atom *asyh)
    209 {
    210 	struct nv50_disp *disp = nv50_disp(head->base.base.dev);
    211 	if ((asyh->core.visible = (asyh->base.cpp != 0))) {
    212 		asyh->core.x = asyh->base.x;
    213 		asyh->core.y = asyh->base.y;
    214 		asyh->core.w = asyh->base.w;
    215 		asyh->core.h = asyh->base.h;
    216 	} else
    217 	if ((asyh->core.visible = (asyh->ovly.cpp != 0)) ||
    218 	    (asyh->core.visible = asyh->curs.visible)) {
    219 		/*XXX: We need to either find some way of having the
    220 		 *     primary base layer appear black, while still
    221 		 *     being able to display the other layers, or we
    222 		 *     need to allocate a dummy black surface here.
    223 		 */
    224 		asyh->core.x = 0;
    225 		asyh->core.y = 0;
    226 		asyh->core.w = asyh->state.mode.hdisplay;
    227 		asyh->core.h = asyh->state.mode.vdisplay;
    228 	}
    229 	asyh->core.handle = disp->core->chan.vram.handle;
    230 	asyh->core.offset = 0;
    231 	asyh->core.format = 0xcf;
    232 	asyh->core.kind = 0;
    233 	asyh->core.layout = 1;
    234 	asyh->core.blockh = 0;
    235 	asyh->core.blocks = 0;
    236 	asyh->core.pitch = ALIGN(asyh->core.w, 64) * 4;
    237 }
    238 
    239 static void
    240 head507d_olut_clr(struct nv50_head *head)
    241 {
    242 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
    243 	u32 *push;
    244 	if ((push = evo_wait(core, 2))) {
    245 		evo_mthd(push, 0x0840 + (head->base.index * 0x400), 1);
    246 		evo_data(push, 0x00000000);
    247 		evo_kick(push, core);
    248 	}
    249 }
    250 
    251 static void
    252 head507d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh)
    253 {
    254 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
    255 	u32 *push;
    256 	if ((push = evo_wait(core, 3))) {
    257 		evo_mthd(push, 0x0840 + (head->base.index * 0x400), 2);
    258 		evo_data(push, 0x80000000 | asyh->olut.mode << 30);
    259 		evo_data(push, asyh->olut.offset >> 8);
    260 		evo_kick(push, core);
    261 	}
    262 }
    263 
    264 #ifdef __NetBSD__
    265 #define	__iomem		__lut_iomem
    266 #define	readw(p)	atomic_load_relaxed((const __iomem uint16_t *)(p))
    267 #define	writew(v,p)	atomic_store_relaxed((__iomem uint16_t *)(p), (v))
    268 #endif
    269 
    270 static void
    271 head507d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem)
    272 {
    273 	for (; size--; in++, mem += 8) {
    274 		writew(drm_color_lut_extract(in->  red, 11) << 3, mem + 0);
    275 		writew(drm_color_lut_extract(in->green, 11) << 3, mem + 2);
    276 		writew(drm_color_lut_extract(in-> blue, 11) << 3, mem + 4);
    277 	}
    278 
    279 	/* INTERPOLATE modes require a "next" entry to interpolate with,
    280 	 * so we replicate the last entry to deal with this for now.
    281 	 */
    282 	writew(readw(mem - 8), mem + 0);
    283 	writew(readw(mem - 6), mem + 2);
    284 	writew(readw(mem - 4), mem + 4);
    285 }
    286 
    287 bool
    288 head507d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size)
    289 {
    290 	if (size != 256)
    291 		return false;
    292 
    293 	if (asyh->base.cpp == 1)
    294 		asyh->olut.mode = 0;
    295 	else
    296 		asyh->olut.mode = 1;
    297 
    298 	asyh->olut.load = head507d_olut_load;
    299 	return true;
    300 }
    301 
    302 void
    303 head507d_mode(struct nv50_head *head, struct nv50_head_atom *asyh)
    304 {
    305 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
    306 	struct nv50_head_mode *m = &asyh->mode;
    307 	u32 *push;
    308 	if ((push = evo_wait(core, 13))) {
    309 		evo_mthd(push, 0x0804 + (head->base.index * 0x400), 2);
    310 		evo_data(push, 0x00800000 | m->clock);
    311 		evo_data(push, m->interlace ? 0x00000002 : 0x00000000);
    312 		evo_mthd(push, 0x0810 + (head->base.index * 0x400), 7);
    313 		evo_data(push, 0x00000000);
    314 		evo_data(push, m->v.active  << 16 | m->h.active );
    315 		evo_data(push, m->v.synce   << 16 | m->h.synce  );
    316 		evo_data(push, m->v.blanke  << 16 | m->h.blanke );
    317 		evo_data(push, m->v.blanks  << 16 | m->h.blanks );
    318 		evo_data(push, m->v.blank2e << 16 | m->v.blank2s);
    319 		evo_data(push, asyh->mode.v.blankus);
    320 		evo_mthd(push, 0x082c + (head->base.index * 0x400), 1);
    321 		evo_data(push, 0x00000000);
    322 		evo_kick(push, core);
    323 	}
    324 }
    325 
    326 void
    327 head507d_view(struct nv50_head *head, struct nv50_head_atom *asyh)
    328 {
    329 	struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan;
    330 	u32 *push;
    331 	if ((push = evo_wait(core, 7))) {
    332 		evo_mthd(push, 0x08a4 + (head->base.index * 0x400), 1);
    333 		evo_data(push, 0x00000000);
    334 		evo_mthd(push, 0x08c8 + (head->base.index * 0x400), 1);
    335 		evo_data(push, asyh->view.iH << 16 | asyh->view.iW);
    336 		evo_mthd(push, 0x08d8 + (head->base.index * 0x400), 2);
    337 		evo_data(push, asyh->view.oH << 16 | asyh->view.oW);
    338 		evo_data(push, asyh->view.oH << 16 | asyh->view.oW);
    339 		evo_kick(push, core);
    340 	}
    341 }
    342 
    343 const struct nv50_head_func
    344 head507d = {
    345 	.view = head507d_view,
    346 	.mode = head507d_mode,
    347 	.olut = head507d_olut,
    348 	.olut_size = 256,
    349 	.olut_set = head507d_olut_set,
    350 	.olut_clr = head507d_olut_clr,
    351 	.core_calc = head507d_core_calc,
    352 	.core_set = head507d_core_set,
    353 	.core_clr = head507d_core_clr,
    354 	.curs_layout = head507d_curs_layout,
    355 	.curs_format = head507d_curs_format,
    356 	.curs_set = head507d_curs_set,
    357 	.curs_clr = head507d_curs_clr,
    358 	.base = head507d_base,
    359 	.ovly = head507d_ovly,
    360 	.dither = head507d_dither,
    361 	.procamp = head507d_procamp,
    362 };
    363