1/* $NetBSD: nouveau_dispnv50_headc57d.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_headc57d.c,v 1.4 2021/12/19 10:49:47 riastradh Exp $"); 26 27#include "head.h" 28#include "atom.h" 29#include "core.h" 30 31static void 32headc57d_or(struct nv50_head *head, struct nv50_head_atom *asyh) 33{ 34 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; 35 u32 *push; 36 if ((push = evo_wait(core, 2))) { 37 /*XXX: This is a dirty hack until OR depth handling is 38 * improved later for deep colour etc. 39 */ 40 switch (asyh->or.depth) { 41 case 6: asyh->or.depth = 5; break; 42 case 5: asyh->or.depth = 4; break; 43 case 2: asyh->or.depth = 1; break; 44 case 0: asyh->or.depth = 4; break; 45 default: 46 WARN_ON(1); 47 break; 48 } 49 50 evo_mthd(push, 0x2004 + (head->base.index * 0x400), 1); 51 evo_data(push, 0xfc000001 | 52 asyh->or.depth << 4 | 53 asyh->or.nvsync << 3 | 54 asyh->or.nhsync << 2); 55 evo_kick(push, core); 56 } 57} 58 59static void 60headc57d_procamp(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 *push; 64 if ((push = evo_wait(core, 2))) { 65 evo_mthd(push, 0x2000 + (head->base.index * 0x400), 1); 66#if 0 67 evo_data(push, 0x80000000 | 68 asyh->procamp.sat.sin << 16 | 69 asyh->procamp.sat.cos << 4); 70#else 71 evo_data(push, 0); 72#endif 73 evo_kick(push, core); 74 } 75} 76 77static void 78headc57d_olut_clr(struct nv50_head *head) 79{ 80 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; 81 u32 *push; 82 if ((push = evo_wait(core, 2))) { 83 evo_mthd(push, 0x2288 + (head->base.index * 0x400), 1); 84 evo_data(push, 0x00000000); 85 evo_kick(push, core); 86 } 87} 88 89static void 90headc57d_olut_set(struct nv50_head *head, struct nv50_head_atom *asyh) 91{ 92 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; 93 u32 *push; 94 if ((push = evo_wait(core, 4))) { 95 evo_mthd(push, 0x2280 + (head->base.index * 0x400), 4); 96 evo_data(push, asyh->olut.size << 8 | 97 asyh->olut.mode << 2 | 98 asyh->olut.output_mode); 99 evo_data(push, 0xffffffff); /* FP_NORM_SCALE. */ 100 evo_data(push, asyh->olut.handle); 101 evo_data(push, asyh->olut.offset >> 8); 102 evo_kick(push, core); 103 } 104} 105 106#ifdef __NetBSD__ 107#define __iomem __lut_iomem 108#define readw(p) atomic_load_relaxed((const __iomem uint16_t *)(p)) 109#define writew(v,p) atomic_store_relaxed((__iomem uint16_t *)(p), (v)) 110#endif 111 112static void 113headc57d_olut_load_8(struct drm_color_lut *in, int size, void __iomem *mem) 114{ 115 memset_io(mem, 0x00, 0x20); /* VSS header. */ 116 mem += 0x20; 117 118 while (size--) { 119 u16 r = drm_color_lut_extract(in-> red + 0, 16); 120 u16 g = drm_color_lut_extract(in->green + 0, 16); 121 u16 b = drm_color_lut_extract(in-> blue + 0, 16); 122 u16 ri = 0, gi = 0, bi = 0, i; 123 124 if (in++, size) { 125 ri = (drm_color_lut_extract(in-> red, 16) - r) / 4; 126 gi = (drm_color_lut_extract(in->green, 16) - g) / 4; 127 bi = (drm_color_lut_extract(in-> blue, 16) - b) / 4; 128 } 129 130 for (i = 0; i < 4; i++, mem += 8) { 131 writew(r + ri * i, mem + 0); 132 writew(g + gi * i, mem + 2); 133 writew(b + bi * i, mem + 4); 134 } 135 } 136 137 /* INTERPOLATE modes require a "next" entry to interpolate with, 138 * so we replicate the last entry to deal with this for now. 139 */ 140 writew(readw(mem - 8), mem + 0); 141 writew(readw(mem - 6), mem + 2); 142 writew(readw(mem - 4), mem + 4); 143} 144 145static void 146headc57d_olut_load(struct drm_color_lut *in, int size, void __iomem *mem) 147{ 148 memset_io(mem, 0x00, 0x20); /* VSS header. */ 149 mem += 0x20; 150 151 for (; size--; in++, mem += 0x08) { 152 writew(drm_color_lut_extract(in-> red, 16), mem + 0); 153 writew(drm_color_lut_extract(in->green, 16), mem + 2); 154 writew(drm_color_lut_extract(in-> blue, 16), mem + 4); 155 } 156 157 /* INTERPOLATE modes require a "next" entry to interpolate with, 158 * so we replicate the last entry to deal with this for now. 159 */ 160 writew(readw(mem - 8), mem + 0); 161 writew(readw(mem - 6), mem + 2); 162 writew(readw(mem - 4), mem + 4); 163} 164 165static bool 166headc57d_olut(struct nv50_head *head, struct nv50_head_atom *asyh, int size) 167{ 168 if (size != 0 && size != 256 && size != 1024) 169 return false; 170 171 asyh->olut.mode = 2; /* DIRECT10 */ 172 asyh->olut.size = 4 /* VSS header. */ + 1024 + 1 /* Entries. */; 173 asyh->olut.output_mode = 1; /* INTERPOLATE_ENABLE. */ 174 if (size == 256) 175 asyh->olut.load = headc57d_olut_load_8; 176 else 177 asyh->olut.load = headc57d_olut_load; 178 return true; 179} 180 181static void 182headc57d_mode(struct nv50_head *head, struct nv50_head_atom *asyh) 183{ 184 struct nv50_dmac *core = &nv50_disp(head->base.base.dev)->core->chan; 185 struct nv50_head_mode *m = &asyh->mode; 186 u32 *push; 187 if ((push = evo_wait(core, 12))) { 188 evo_mthd(push, 0x2064 + (head->base.index * 0x400), 5); 189 evo_data(push, (m->v.active << 16) | m->h.active ); 190 evo_data(push, (m->v.synce << 16) | m->h.synce ); 191 evo_data(push, (m->v.blanke << 16) | m->h.blanke ); 192 evo_data(push, (m->v.blanks << 16) | m->h.blanks ); 193 evo_data(push, (m->v.blank2e << 16) | m->v.blank2s); 194 evo_mthd(push, 0x200c + (head->base.index * 0x400), 1); 195 evo_data(push, m->clock * 1000); 196 evo_mthd(push, 0x2028 + (head->base.index * 0x400), 1); 197 evo_data(push, m->clock * 1000); 198 /*XXX: HEAD_USAGE_BOUNDS, doesn't belong here. */ 199 evo_mthd(push, 0x2030 + (head->base.index * 0x400), 1); 200 evo_data(push, 0x00001014); 201 evo_kick(push, core); 202 } 203} 204 205const struct nv50_head_func 206headc57d = { 207 .view = headc37d_view, 208 .mode = headc57d_mode, 209 .olut = headc57d_olut, 210 .olut_identity = true, 211 .olut_size = 1024, 212 .olut_set = headc57d_olut_set, 213 .olut_clr = headc57d_olut_clr, 214 .curs_layout = head917d_curs_layout, 215 .curs_format = headc37d_curs_format, 216 .curs_set = headc37d_curs_set, 217 .curs_clr = headc37d_curs_clr, 218 .dither = headc37d_dither, 219 .procamp = headc57d_procamp, 220 .or = headc57d_or, 221}; 222