Home | History | Annotate | Line # | Download | only in dev
      1 /* $NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2024 Jared McNeill <jmcneill (at) invisible.ca>
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     26  * POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/cdefs.h>
     30 __KERNEL_RCSID(0, "$NetBSD: avenc.c,v 1.2 2024/10/13 16:21:37 jmcneill Exp $");
     31 
     32 #include <sys/param.h>
     33 #include <sys/systm.h>
     34 #include <sys/kernel.h>
     35 #include <sys/device.h>
     36 #include <sys/conf.h>
     37 #include <sys/bus.h>
     38 #include <sys/kmem.h>
     39 #include <machine/wii.h>
     40 
     41 #include <lib/libkern/libkern.h>	/* hexdump */
     42 
     43 #include <dev/i2c/i2cvar.h>
     44 
     45 #include "avenc.h"
     46 #include "vireg.h"
     47 
     48 #define AVENC_DEBUG		0
     49 
     50 #define AVENC_ADDR		0x70
     51 
     52 #define AVENC_VOLUME		0x71
     53 #define  AVENC_VOLUME_RIGHT	__BITS(15,8)
     54 #define  AVENC_VOLUME_LEFT	__BITS(7,0)
     55 
     56 #define RD1			avenc_read_1
     57 #define RD2			avenc_read_2
     58 #define WR1			avenc_write_1
     59 #define WR2			avenc_write_2
     60 #define WR4			avenc_write_4
     61 #define WRBUF			avenc_write_buf
     62 
     63 static i2c_tag_t avenc_tag;
     64 static i2c_addr_t avenc_addr;
     65 
     66 static uint8_t
     67 avenc_read_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg)
     68 {
     69 	uint8_t val;
     70 
     71 	if (iic_smbus_read_byte(tag, addr, reg, &val, 0) != 0) {
     72 		val = 0xff;
     73 	}
     74 
     75 	return val;
     76 }
     77 
     78 static uint16_t
     79 avenc_read_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg)
     80 {
     81 	uint8_t vbuf[2];
     82 
     83 	if (iic_exec(tag, I2C_OP_READ_WITH_STOP, addr, &reg, 1,
     84 	    	     vbuf, sizeof(vbuf), 0) != 0) {
     85 		return 0xffff;
     86 	} else {
     87 		return ((uint16_t)vbuf[0] << 8) | vbuf[1];
     88 	}
     89 }
     90 
     91 static void
     92 avenc_write_1(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t val)
     93 {
     94 	iic_smbus_write_byte(tag, addr, reg, val, 0);
     95 }
     96 
     97 static void
     98 avenc_write_2(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint16_t val)
     99 {
    100 	uint8_t vbuf[2];
    101 
    102 	vbuf[0] = (val >> 8) & 0xff;
    103 	vbuf[1] = val & 0xff;
    104 
    105 	iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &reg, 1,
    106 		 vbuf, sizeof(vbuf), 0);
    107 }
    108 
    109 static void
    110 avenc_write_4(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint32_t val)
    111 {
    112 	uint8_t vbuf[4];
    113 
    114 	vbuf[0] = (val >> 24) & 0xff;
    115 	vbuf[1] = (val >> 16) & 0xff;
    116 	vbuf[2] = (val >> 8) & 0xff;
    117 	vbuf[3] = val & 0xff;
    118 
    119 	iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &reg, 1,
    120 		 vbuf, sizeof(vbuf), 0);
    121 }
    122 
    123 static void
    124 avenc_write_buf(i2c_tag_t tag, i2c_addr_t addr, uint8_t reg, uint8_t *buf,
    125     size_t buflen)
    126 {
    127 	iic_exec(tag, I2C_OP_WRITE_WITH_STOP, addr, &reg, 1,
    128 		 buf, buflen, 0);
    129 }
    130 
    131 void
    132 avenc_get_volume(uint8_t *left, uint8_t *right)
    133 {
    134 	uint16_t val;
    135 
    136 	if (avenc_addr == 0) {
    137 		*left = *right = 0;
    138 		return;
    139 	}
    140 
    141 	iic_acquire_bus(avenc_tag, 0);
    142 	val = RD2(avenc_tag, avenc_addr, AVENC_VOLUME);
    143 	iic_release_bus(avenc_tag, 0);
    144 
    145 	*left = __SHIFTOUT(val, AVENC_VOLUME_LEFT);
    146 	*right = __SHIFTOUT(val, AVENC_VOLUME_RIGHT);
    147 }
    148 
    149 void
    150 avenc_set_volume(uint8_t left, uint8_t right)
    151 {
    152 	uint16_t val;
    153 
    154 	if (avenc_addr == 0) {
    155 		return;
    156 	}
    157 
    158 	val = __SHIFTIN(left, AVENC_VOLUME_LEFT) |
    159 	      __SHIFTIN(right, AVENC_VOLUME_RIGHT);
    160 
    161 	iic_acquire_bus(avenc_tag, 0);
    162 	WR2(avenc_tag, avenc_addr, AVENC_VOLUME, val);
    163 	iic_release_bus(avenc_tag, 0);
    164 }
    165 
    166 static void
    167 avenc_dump_regs(const char *pfx, i2c_tag_t tag, i2c_addr_t addr)
    168 {
    169 	uint8_t regdump[0x100];
    170 	unsigned int reg;
    171 
    172 	iic_acquire_bus(tag, 0);
    173 
    174 	for (reg = 0; reg < sizeof(regdump); reg++) {
    175 		regdump[reg] = RD1(tag, addr, reg);
    176 	}
    177 
    178 	iic_release_bus(tag, 0);
    179 
    180 	hexdump(printf, pfx, regdump, sizeof(regdump));
    181 }
    182 
    183 static void
    184 avenc_init(i2c_tag_t tag, i2c_addr_t addr)
    185 {
    186 	uint8_t mvinit[26] = {};
    187 	uint8_t ginit[] = {
    188 		0x10, 0x00, 0x10, 0x00, 0x10, 0x00, 0x10, 0x00,
    189 		0x10, 0x00, 0x10, 0x00, 0x10, 0x20, 0x40, 0x60,
    190 		0x80, 0xa0, 0xeb, 0x10, 0x00, 0x20, 0x00, 0x40,
    191 		0x00, 0x60, 0x00, 0x80, 0x00, 0xa0, 0x00, 0xeb,
    192 		0x00
    193 	};
    194 	uint8_t video_fmt;
    195 
    196 	video_fmt = 0;
    197 	if (__SHIFTOUT(in16(VI_BASE + VI_DCR), VI_DCR_FMT) == VI_DCR_FMT_PAL) {
    198 		video_fmt |= 0x02;
    199 	}
    200 	if ((in16(VI_BASE + VI_VISEL) & VI_VISEL_COMPONENT_CABLE) != 0) {
    201 		video_fmt |= 0x20;
    202 	}
    203 
    204 	iic_acquire_bus(tag, 0);
    205 	WR1(tag, addr, 0x6a, 1);
    206 	WR1(tag, addr, 0x65, 3);
    207 	WR1(tag, addr, 0x01, video_fmt);
    208 	WR1(tag, addr, 0x00, 0);
    209 	WR1(tag, addr, 0x02, 7);
    210 	WR2(tag, addr, 0x05, 0);
    211 	WR2(tag, addr, 0x08, 0);
    212 	WR4(tag, addr, 0x7a, 0);
    213 	WRBUF(tag, addr, 0x40, mvinit, sizeof(mvinit));
    214 	WR1(tag, addr, 0x0a, 0);
    215 	WR1(tag, addr, 0x03, 1);
    216 	WRBUF(tag, addr, 0x10, ginit, sizeof(ginit));
    217 	WR1(tag, addr, 0x04, 1);
    218 	WR4(tag, addr, 0x7a, 0);
    219 	WR2(tag, addr, 0x08, 0);
    220 	WR1(tag, addr, 0x03, 1);
    221 	WR1(tag, addr, 0x6e, 0);
    222 	iic_release_bus(tag, 0);
    223 
    224 	avenc_set_volume(0x8e, 0x8e);
    225 }
    226 
    227 
    228 static int
    229 avenc_match(device_t parent, cfdata_t match, void *aux)
    230 {
    231 	struct i2c_attach_args *ia = aux;
    232 
    233 	return ia->ia_addr == AVENC_ADDR;
    234 }
    235 
    236 static void
    237 avenc_attach(device_t parent, device_t self, void *aux)
    238 {
    239 	struct i2c_attach_args *ia = aux;
    240 	i2c_tag_t tag = ia->ia_tag;
    241 	i2c_addr_t addr = ia->ia_addr;
    242 
    243 	KASSERT(device_unit(self) == 0);
    244 
    245 	aprint_naive("\n");
    246 	aprint_normal(": A/V Encoder\n");
    247 
    248 	if (AVENC_DEBUG) {
    249 		avenc_dump_regs("avenc pre ", tag, addr);
    250 	}
    251 
    252 	avenc_tag = tag;
    253 	avenc_addr = addr;
    254 
    255 	avenc_init(tag, addr);
    256 
    257 	if (AVENC_DEBUG) {
    258 		avenc_dump_regs("avenc post", tag, addr);
    259 	}
    260 }
    261 
    262 CFATTACH_DECL_NEW(avenc, 0, avenc_match, avenc_attach, NULL, NULL);
    263