#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/mman.h>
#include <sys/module.h>
#include <sys/event.h>
#include <sys/proc.h>
#include <sys/signalvar.h>
#include <sys/thread2.h>
#include <sys/vnode.h>
#include <sys/resource.h>
#include <sys/bus.h>
#include <sys/rman.h>
#include <machine/clock.h>
#include <dev/video/meteor/ioctl_meteor.h>
#include <dev/video/bktr/ioctl_bt848.h>
#include <bus/pci/pcireg.h>
#include <bus/pci/pcivar.h>
#include <dev/video/cxm/cxm.h>
#include <bus/iicbus/iiconf.h>
static struct cxm_dev cxm_devs[] = {
{ PCI_VENDOR_ICOMPRESSION, PCI_PRODUCT_ICOMPRESSION_ITVC15,
"Conexant iTVC15 MPEG Coder" },
{ PCI_VENDOR_ICOMPRESSION, PCI_PRODUCT_ICOMPRESSION_ITVC16,
"Conexant iTVC16 MPEG Coder" },
{ 0, 0, NULL }
};
static int cxm_probe(device_t dev);
static int cxm_attach(device_t dev);
static int cxm_detach(device_t dev);
static int cxm_shutdown(device_t dev);
static void cxm_intr(void *arg);
static void cxm_child_detached(device_t dev, device_t child);
static int cxm_read_ivar(device_t bus, device_t dev,
int index, uintptr_t* val);
static int cxm_write_ivar(device_t bus, device_t dev,
int index, uintptr_t val);
static device_method_t cxm_methods[] = {
DEVMETHOD(device_probe, cxm_probe),
DEVMETHOD(device_attach, cxm_attach),
DEVMETHOD(device_detach, cxm_detach),
DEVMETHOD(device_shutdown, cxm_shutdown),
DEVMETHOD(bus_child_detached, cxm_child_detached),
DEVMETHOD(bus_print_child, bus_generic_print_child),
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
DEVMETHOD(bus_read_ivar, cxm_read_ivar),
DEVMETHOD(bus_write_ivar, cxm_write_ivar),
DEVMETHOD_END
};
static driver_t cxm_driver = {
"cxm",
cxm_methods,
sizeof(struct cxm_softc),
};
static devclass_t cxm_devclass;
static d_open_t cxm_open;
static d_close_t cxm_close;
static d_read_t cxm_read;
static d_ioctl_t cxm_ioctl;
static d_kqfilter_t cxm_kqfilter;
static void cxm_filter_detach(struct knote *);
static int cxm_filter(struct knote *, long);
static struct dev_ops cxm_ops = {
{ "cxm", 0, 0 },
.d_open = cxm_open,
.d_close = cxm_close,
.d_read = cxm_read,
.d_ioctl = cxm_ioctl,
.d_kqfilter = cxm_kqfilter
};
MODULE_DEPEND(cxm, cxm_iic, 1, 1, 1);
DRIVER_MODULE(cxm, pci, cxm_driver, cxm_devclass, NULL, NULL);
static struct cxm_codec_audio_format codec_audio_formats[] = {
{ 44100, 0xb8 },
{ 48000, 0xe9 }
};
static struct cxm_codec_profile vcd_ntsc_profile = {
"MPEG-1 VideoCD NTSC video and MPEG audio",
CXM_FW_STREAM_TYPE_VCD,
30,
352, 240, 480,
{ 10, 12, 21 },
12,
0,
{ 1, 1150000, 0 },
{ 1, 15, 3},
{ 0, 3, 1, 4 },
44100
};
static struct cxm_codec_profile vcd_pal_profile = {
"MPEG-1 VideoCD PAL video and MPEG audio",
CXM_FW_STREAM_TYPE_VCD,
25,
352, 288, 576,
{ 6, 17, 22 },
8,
0,
{ 1, 1150000, 0 },
{ 1, 12, 3},
{ 0, 3, 1, 4 },
44100
};
static struct cxm_codec_profile svcd_ntsc_profile = {
"MPEG-2 SuperVCD NTSC video and MPEG audio",
CXM_FW_STREAM_TYPE_SVCD,
30,
480, 480, 480,
{ 10, 12, 21 },
2,
0,
{ 0, 1150000, 2500000 },
{ 1, 15, 3},
{ 0, 3, 1, 4 },
44100
};
static struct cxm_codec_profile svcd_pal_profile = {
"MPEG-2 SuperVCD PAL video and MPEG audio",
CXM_FW_STREAM_TYPE_SVCD,
25,
480, 576, 576,
{ 6, 17, 22 },
2,
0,
{ 0, 1150000, 2500000 },
{ 1, 12, 3},
{ 0, 3, 1, 4 },
44100
};
static struct cxm_codec_profile dvd_half_d1_ntsc_profile = {
"MPEG-2 DVD NTSC video and MPEG audio",
CXM_FW_STREAM_TYPE_DVD,
30,
352, 480, 480,
{ 10, 12, 21 },
2,
0,
{ 0, 4000000, 4520000 },
{ 1, 15, 3},
{ 0, 3, 1, 4 },
48000
};
static struct cxm_codec_profile dvd_half_d1_pal_profile = {
"MPEG-2 DVD PAL video and MPEG audio",
CXM_FW_STREAM_TYPE_DVD,
25,
352, 576, 576,
{ 6, 17, 22 },
2,
0,
{ 0, 4000000, 4520000 },
{ 1, 12, 3},
{ 0, 3, 1, 4 },
48000
};
static struct cxm_codec_profile dvd_full_d1_ntsc_profile = {
"MPEG-2 DVD NTSC video and MPEG audio",
CXM_FW_STREAM_TYPE_DVD,
30,
720, 480, 480,
{ 10, 12, 21 },
2,
0,
{ 0, 9000000, 9520000 },
{ 1, 15, 3},
{ 0, 3, 1, 4 },
48000
};
static struct cxm_codec_profile dvd_full_d1_pal_profile = {
"MPEG-2 DVD PAL video and MPEG audio",
CXM_FW_STREAM_TYPE_DVD,
25,
720, 576, 576,
{ 6, 17, 22 },
2,
0,
{ 0, 9000000, 9520000 },
{ 1, 12, 3},
{ 0, 3, 1, 4 },
48000
};
static const struct cxm_codec_profile
*codec_profiles[] = {
&vcd_ntsc_profile,
&vcd_pal_profile,
&svcd_ntsc_profile,
&svcd_pal_profile,
&dvd_half_d1_ntsc_profile,
&dvd_half_d1_pal_profile,
&dvd_full_d1_ntsc_profile,
&dvd_full_d1_pal_profile
};
static unsigned int
cxm_queue_firmware_command(struct cxm_softc *sc,
enum cxm_mailbox_name mbx_name, uint32_t cmd,
uint32_t *parameters, unsigned int nparameters)
{
unsigned int i;
unsigned int mailbox;
uint32_t completed_command;
uint32_t flags;
if (nparameters > CXM_MBX_MAX_PARAMETERS) {
device_printf(sc->dev, "too many parameters for mailbox\n");
return -1;
}
mailbox = 0;
switch (mbx_name) {
case cxm_dec_mailbox:
mailbox = sc->dec_mbx
+ CXM_MBX_FW_CMD_MAILBOX *sizeof(struct cxm_mailbox);
break;
case cxm_enc_mailbox:
mailbox = sc->enc_mbx
+ CXM_MBX_FW_CMD_MAILBOX *sizeof(struct cxm_mailbox);
break;
default:
return -1;
}
crit_enter();
for (i = 0; i < CXM_MBX_FW_CMD_MAILBOXES; i++) {
flags = CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, flags));
if (!(flags & CXM_MBX_FLAG_IN_USE))
break;
if ((flags & (CXM_MBX_FLAG_DRV_DONE | CXM_MBX_FLAG_FW_DONE))
== (CXM_MBX_FLAG_DRV_DONE | CXM_MBX_FLAG_FW_DONE)) {
completed_command
= CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, command));
if (completed_command == CXM_FW_CMD_SCHED_DMA_TO_HOST)
break;
}
mailbox += sizeof(struct cxm_mailbox);
}
if (i >= CXM_MBX_FW_CMD_MAILBOXES) {
crit_exit();
return -1;
}
CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags),
CXM_MBX_FLAG_IN_USE);
CSR_READ_4(sc, mailbox + offsetof(struct cxm_mailbox, flags));
crit_exit();
CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, command), cmd);
CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, timeout),
CXM_FW_STD_TIMEOUT);
for (i = 0; i < nparameters; i++)
CSR_WRITE_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, parameters)
+ i * sizeof(uint32_t),
*(parameters + i));
for (; i < CXM_MBX_MAX_PARAMETERS; i++)
CSR_WRITE_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, parameters)
+ i * sizeof(uint32_t), 0);
CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags),
CXM_MBX_FLAG_IN_USE | CXM_MBX_FLAG_DRV_DONE);
return mailbox;
}
static int
cxm_firmware_command(struct cxm_softc *sc,
enum cxm_mailbox_name mbx_name, uint32_t cmd,
uint32_t *parameters, unsigned int nparameters)
{
const char *wmesg;
unsigned int *bmp;
unsigned int i;
unsigned int mailbox;
uint32_t flags;
uint32_t result;
bmp = NULL;
wmesg = "";
switch (mbx_name) {
case cxm_dec_mailbox:
bmp = &sc->dec_mbx;
wmesg = "cxmdfw";
break;
case cxm_enc_mailbox:
bmp = &sc->enc_mbx;
wmesg = "cxmefw";
break;
default:
return -1;
}
mailbox = cxm_queue_firmware_command(sc, mbx_name, cmd,
parameters, nparameters);
if (mailbox == -1) {
device_printf(sc->dev, "no free mailboxes\n");
return -1;
}
tsleep(bmp, 0, wmesg, hz / 100);
for (i = 0; i < 100; i++) {
flags = CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, flags));
if ((flags & CXM_MBX_FLAG_FW_DONE))
break;
tsleep(bmp, 0, wmesg, hz / 100);
}
if (i >= 100) {
device_printf(sc->dev, "timeout\n");
return -1;
}
result = CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, result));
for (i = 0; i < nparameters; i++)
*(parameters + i)
= CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, parameters)
+ i * sizeof(uint32_t));
CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags), 0);
return result == 0 ? 0 : -1;
}
static int
cxm_firmware_command_nosleep(struct cxm_softc *sc,
enum cxm_mailbox_name mbx_name, uint32_t cmd,
uint32_t *parameters, unsigned int nparameters)
{
unsigned int i;
unsigned int mailbox;
uint32_t flags;
uint32_t result;
for (i = 0; i < 100; i++) {
mailbox = cxm_queue_firmware_command(sc, mbx_name, cmd,
parameters, nparameters);
if (mailbox != -1)
break;
DELAY(10000);
}
if (i >= 100) {
device_printf(sc->dev, "no free mailboxes\n");
return -1;
}
DELAY(10000);
for (i = 0; i < 100; i++) {
flags = CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, flags));
if ((flags & CXM_MBX_FLAG_FW_DONE))
break;
DELAY(10000);
}
if (i >= 100) {
device_printf(sc->dev, "timeout\n");
return -1;
}
result = CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, result));
for (i = 0; i < nparameters; i++)
*(parameters + i)
= CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, parameters)
+ i * sizeof(uint32_t));
CSR_WRITE_4(sc, mailbox + offsetof(struct cxm_mailbox, flags), 0);
return result == 0 ? 0 : -1;
}
static int
cxm_stop_firmware(struct cxm_softc *sc)
{
if (cxm_firmware_command_nosleep(sc, cxm_enc_mailbox,
CXM_FW_CMD_ENC_HALT_FW, NULL, 0) < 0)
return -1;
if (sc->type == cxm_iTVC15_type
&& cxm_firmware_command_nosleep(sc, cxm_dec_mailbox,
CXM_FW_CMD_DEC_HALT_FW,
NULL, 0) < 0)
return -1;
DELAY(10000);
return 0;
}
static void
cxm_set_irq_mask(struct cxm_softc *sc, uint32_t mask)
{
crit_enter();
CSR_WRITE_4(sc, CXM_REG_IRQ_MASK, mask);
CSR_READ_4(sc, CXM_REG_IRQ_MASK);
sc->irq_mask = mask;
crit_exit();
}
static void
cxm_set_irq_status(struct cxm_softc *sc, uint32_t status)
{
CSR_WRITE_4(sc, CXM_REG_IRQ_STATUS, status);
CSR_READ_4(sc, CXM_REG_IRQ_STATUS);
}
static int
cxm_stop_hardware(struct cxm_softc *sc)
{
if (sc->cxm_iic) {
if (cxm_saa7115_mute(sc) < 0)
return -1;
if (cxm_msp_mute(sc) < 0)
return -1;
}
if (sc->enc_mbx != -1) {
if (cxm_stop_firmware(sc) < 0)
return -1;
}
cxm_set_irq_mask(sc, 0xffffffff);
CSR_WRITE_4(sc, CXM_REG_VDM, CXM_CMD_VDM_STOP);
CSR_WRITE_4(sc, CXM_REG_AO, CXM_CMD_AO_STOP);
CSR_WRITE_4(sc, CXM_REG_APU, CXM_CMD_APU_PING);
CSR_WRITE_4(sc, CXM_REG_VPU, sc->type == cxm_iTVC15_type
? CXM_CMD_VPU_STOP15
: CXM_CMD_VPU_STOP16);
CSR_WRITE_4(sc, CXM_REG_HW_BLOCKS, CXM_CMD_HW_BLOCKS_RST);
CSR_WRITE_4(sc, CXM_REG_SPU, CXM_CMD_SPU_STOP);
DELAY(10000);
return 0;
}
static int
cxm_download_firmware(struct cxm_softc *sc)
{
unsigned int i;
const uint32_t *fw;
if (strncmp((const char *)cxm_enc_fw, "NOFW", 4) == 0) {
device_printf(sc->dev, "encoder firmware not compiled in\n");
return -1;
} else if (strncmp((const char *)cxm_dec_fw, "NOFW", 4) == 0) {
device_printf(sc->dev, "decoder firmware not compiled in\n");
return -1;
}
fw = (const uint32_t *)cxm_enc_fw;
for (i = 0; i < CXM_FW_SIZE; i += sizeof(*fw))
CSR_WRITE_4(sc, CXM_MEM_ENC + i, *fw++);
if (sc->type == cxm_iTVC15_type) {
fw = (const uint32_t *)cxm_dec_fw;
for (i = 0; i < CXM_FW_SIZE; i += sizeof(*fw))
CSR_WRITE_4(sc, CXM_MEM_DEC + i, *fw++);
}
return 0;
}
static int
cxm_init_hardware(struct cxm_softc *sc)
{
unsigned int i;
unsigned int mailbox;
uint32_t parameter;
if (cxm_stop_hardware(sc) < 0)
return -1;
CSR_WRITE_4(sc, CXM_REG_ENC_SDRAM_PRECHARGE,
CXM_CMD_SDRAM_PRECHARGE_INIT);
CSR_WRITE_4(sc, CXM_REG_ENC_SDRAM_REFRESH,
CXM_CMD_SDRAM_REFRESH_INIT);
CSR_WRITE_4(sc, CXM_REG_DEC_SDRAM_PRECHARGE,
CXM_CMD_SDRAM_PRECHARGE_INIT);
CSR_WRITE_4(sc, CXM_REG_DEC_SDRAM_REFRESH,
CXM_CMD_SDRAM_REFRESH_INIT);
DELAY(600000);
if (cxm_download_firmware(sc) < 0)
return -1;
CSR_WRITE_4(sc, CXM_REG_SPU,
CSR_READ_4(sc, CXM_REG_SPU) & CXM_MASK_SPU_ENABLE);
DELAY(1000000);
CSR_WRITE_4(sc, CXM_REG_VPU,
CSR_READ_4(sc, CXM_REG_VPU)
& (sc->type == cxm_iTVC15_type
? CXM_MASK_VPU_ENABLE15
: CXM_MASK_VPU_ENABLE16));
DELAY(1000000);
mailbox = CXM_MEM_ENC;
for (i = 0; i < CXM_MEM_ENC_SIZE; i += 0x100)
if (CSR_READ_4(sc, mailbox + i) == 0x12345678
&& CSR_READ_4(sc, mailbox + i + 4) == 0x34567812
&& CSR_READ_4(sc, mailbox + i + 8) == 0x56781234
&& CSR_READ_4(sc, mailbox + i + 12) == 0x78123456)
break;
if (i >= CXM_MEM_ENC_SIZE)
return -1;
sc->enc_mbx = mailbox + i + 16;
if (sc->type == cxm_iTVC15_type) {
mailbox = CXM_MEM_DEC;
for (i = 0; i < CXM_MEM_DEC_SIZE; i += 0x100)
if (CSR_READ_4(sc, mailbox + i) == 0x12345678
&& CSR_READ_4(sc, mailbox + i + 4) == 0x34567812
&& CSR_READ_4(sc, mailbox + i + 8) == 0x56781234
&& CSR_READ_4(sc, mailbox + i + 12) == 0x78123456)
break;
if (i >= CXM_MEM_DEC_SIZE)
return -1;
sc->dec_mbx = mailbox + i + 16;
}
parameter = 0;
if (cxm_firmware_command_nosleep(sc, cxm_enc_mailbox,
CXM_FW_CMD_ENC_GET_FW_VER,
¶meter, 1) < 0)
return -1;
device_printf(sc->dev, "encoder firmware version %#x\n",
(unsigned int)parameter);
if (sc->type == cxm_iTVC15_type) {
parameter = 0;
if (cxm_firmware_command_nosleep(sc, cxm_dec_mailbox,
CXM_FW_CMD_DEC_GET_FW_VER,
¶meter, 1) < 0)
return -1;
device_printf(sc->dev, "decoder firmware version %#x\n",
(unsigned int)parameter);
}
return 0;
}
static int
cxm_configure_encoder(struct cxm_softc *sc)
{
int fps;
unsigned int i;
uint32_t parameters[12];
const struct cxm_codec_profile *cpp;
if (sc->source == cxm_fm_source)
switch (cxm_tuner_selected_channel_set(sc)) {
case CHNLSET_NABCST:
case CHNLSET_CABLEIRC:
case CHNLSET_JPNBCST:
case CHNLSET_JPNCABLE:
fps = 30;
break;
default:
fps = 25;
break;
}
else
fps = cxm_saa7115_detected_fps(sc);
if (fps < 0)
return -1;
if (sc->profile->fps != fps) {
for (i = 0; i < NUM_ELEMENTS(codec_profiles); i++)
if (codec_profiles[i]->fps == fps
&& codec_profiles[i]->stream_type
== sc->profile->stream_type
&& codec_profiles[i]->width == sc->profile->width)
break;
if (i >= NUM_ELEMENTS(codec_profiles))
return -1;
sc->profile = codec_profiles[i];
}
cpp = sc->profile;
if (cxm_saa7115_configure(sc,
cpp->width, cpp->source_height, fps,
cpp->audio_sample_rate) < 0)
return -1;
parameters[0] = 1;
parameters[1] = 1;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_DMA_BLOCKLEN,
parameters, 2) != 0)
return -1;
parameters[0] = 0;
parameters[1] = 0;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_PGM_INDEX_INFO,
parameters, 2) != 0)
return -1;
parameters[0] = cpp->stream_type;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_STREAM_TYPE,
parameters, 1) != 0)
return -1;
parameters[0] = 0;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_OUTPUT_PORT,
parameters, 1) != 0)
return -1;
parameters[0] = cpp->fps == 30 ? 0 : 1;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_FRAME_RATE,
parameters, 1) != 0)
return -1;
parameters[0] = cpp->height;
parameters[1] = cpp->width;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_FRAME_SIZE,
parameters, 2) != 0)
return -1;
parameters[0] = cpp->aspect;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_ASPECT_RATIO,
parameters, 1) != 0)
return -1;
parameters[0] = cpp->bitrate.mode;
parameters[1] = cpp->bitrate.average;
parameters[2] = cpp->bitrate.peak / 400;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_BITRATES,
parameters, 3) != 0)
return -1;
parameters[0] = cpp->gop.closure;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_GOP_CLOSURE,
parameters, 1) != 0)
return -1;
parameters[0] = cpp->gop.frames;
parameters[1] = cpp->gop.bframes;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_GOP_PROPERTIES,
parameters, 2) != 0)
return -1;
parameters[0] = cpp->pulldown;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_3_2_PULLDOWN,
parameters, 1) != 0)
return -1;
parameters[0] = cpp->dnr.mode;
parameters[1] = cpp->dnr.type;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_DNR_FILTER_MODE,
parameters, 2) != 0)
return -1;
parameters[0] = cpp->dnr.spatial;
parameters[1] = cpp->dnr.temporal;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_DNR_FILTER_PROPERTIES,
parameters, 2) != 0)
return -1;
for (i = 0; i < NUM_ELEMENTS(codec_audio_formats); i++)
if (codec_audio_formats[i].sample_rate
== cpp->audio_sample_rate)
break;
if (i >= NUM_ELEMENTS(codec_audio_formats))
return -1;
parameters[0] = codec_audio_formats[i].format;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_AUDIO_PROPERTIES,
parameters, 1) != 0)
return -1;
parameters[0] = 0;
parameters[1] = 255;
parameters[2] = 0;
parameters[3] = 255;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_CORING_LEVELS,
parameters, 4) != 0)
return -1;
parameters[0] = 3;
parameters[1] = 1;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_SPATIAL_FILTER_TYPE,
parameters, 2) != 0)
return -1;
parameters[0] = 0;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_FRAME_DROP_RATE,
parameters, 1) != 0)
return -1;
parameters[0] = 0;
parameters[1] = 0;
parameters[2] = 0;
parameters[3] = 0;
parameters[4] = 0;
parameters[5] = 0;
parameters[6] = 0;
parameters[7] = 0;
parameters[8] = 0;
parameters[9] = 0;
parameters[10] = 0;
parameters[11] = 0;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_PLACEHOLDER,
parameters, 12) != 0)
return -1;
parameters[0] = 0xbd04;
parameters[1] = 0;
parameters[2] = 0;
parameters[3] = 0x25256262;
parameters[4] = 0x38387f7f;
parameters[5] = cpp->vbi.nlines;
parameters[6] = 1440;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_VBI_PROPERTIES,
parameters, 7) != 0)
return -1;
parameters[0] = 0xffffffff;
parameters[1] = 0;
parameters[2] = 0;
parameters[3] = 0;
parameters[4] = 0;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_VBI_LINE,
parameters, 5) != 0)
return -1;
parameters[0] = cpp->source_height / 2 + cpp->vbi.nlines;
parameters[1] = cpp->source_height / 2 + cpp->vbi.nlines;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ASSIGN_NUM_VSYNC_LINES,
parameters, 2) != 0)
return -1;
return 0;
}
static int
cxm_start_encoder(struct cxm_softc *sc)
{
uint32_t parameters[4];
uint32_t subtype;
uint32_t type;
if (sc->encoding)
return 0;
if (cxm_configure_encoder(sc) < 0)
return -1;
parameters[0] = sc->source == cxm_fm_source ? 1 : 0;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_MUTE_VIDEO_INPUT,
parameters, 1) != 0)
return -1;
cxm_set_irq_status(sc, CXM_IRQ_ENC);
parameters[0] = 0;
parameters[1] = 1;
parameters[2] = 0x10000000;
parameters[3] = -1;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ENC_EVENT_NOTIFICATION,
parameters, 4) != 0)
return -1;
if (cxm_saa7115_mute(sc) < 0)
return -1;
if (cxm_msp_mute(sc) < 0)
return -1;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_INITIALIZE_VIDEO_INPUT,
NULL, 0) < 0)
return -1;
if (cxm_saa7115_unmute(sc) < 0)
return -1;
if (cxm_msp_unmute(sc) < 0)
return -1;
tsleep(&sc->encoding, 0, "cxmce", hz / 10);
type = sc->mpeg ? CXM_FW_CAPTURE_STREAM_TYPE_MPEG
: CXM_FW_CAPTURE_STREAM_TYPE_RAW;
subtype = ((sc->mpeg || sc->source == cxm_fm_source)
? CXM_FW_CAPTURE_STREAM_PCM_AUDIO : 0)
| ((sc->mpeg || sc->source != cxm_fm_source)
? CXM_FW_CAPTURE_STREAM_YUV : 0);
parameters[0] = type;
parameters[1] = subtype;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_BEGIN_CAPTURE, parameters, 2) != 0)
return -1;
sc->enc_pool.offset = 0;
sc->enc_pool.read = 0;
sc->enc_pool.write = 0;
sc->encoding_eos = 0;
sc->encoding = 1;
cxm_set_irq_mask(sc, sc->irq_mask & ~CXM_IRQ_ENC);
return 0;
}
static int
cxm_stop_encoder(struct cxm_softc *sc)
{
uint32_t parameters[4];
uint32_t subtype;
uint32_t type;
if (!sc->encoding)
return 0;
type = sc->mpeg ? CXM_FW_CAPTURE_STREAM_TYPE_MPEG
: CXM_FW_CAPTURE_STREAM_TYPE_RAW;
subtype = ((sc->mpeg || sc->source == cxm_fm_source)
? CXM_FW_CAPTURE_STREAM_PCM_AUDIO : 0)
| ((sc->mpeg || sc->source != cxm_fm_source)
? CXM_FW_CAPTURE_STREAM_YUV : 0);
parameters[0] = sc->mpeg ? 0 : 1;
parameters[1] = type;
parameters[2] = subtype;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_END_CAPTURE, parameters, 3) != 0)
return -1;
crit_enter();
if (!sc->encoding_eos)
tsleep(&sc->encoding_eos, 0, "cxmeos", hz);
crit_exit();
if (sc->mpeg && !sc->encoding_eos)
device_printf(sc->dev, "missing encoder EOS\n");
parameters[0] = 0;
parameters[1] = 0;
parameters[2] = 0x10000000;
parameters[3] = -1;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_ENC_EVENT_NOTIFICATION,
parameters, 4) != 0)
return -1;
cxm_set_irq_mask(sc, sc->irq_mask | CXM_IRQ_ENC);
sc->encoding = 0;
return 0;
}
static int
cxm_pause_encoder(struct cxm_softc *sc)
{
uint32_t parameter;
parameter = 0;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_PAUSE_ENCODER, ¶meter, 1) != 0)
return -1;
return 0;
}
static int
cxm_unpause_encoder(struct cxm_softc *sc)
{
uint32_t parameter;
parameter = 1;
if (cxm_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_PAUSE_ENCODER, ¶meter, 1) != 0)
return -1;
return 0;
}
static unsigned int
cxm_encoder_fixup_byte_order(struct cxm_softc *sc,
unsigned int current, size_t offset)
{
unsigned int strips;
unsigned int i;
unsigned int j;
unsigned int k;
unsigned int macroblocks_per_line;
unsigned int scratch;
unsigned int words_per_line;
uint32_t *ptr;
uint32_t *src;
size_t nbytes;
switch (sc->enc_pool.bufs[current].byte_order) {
case cxm_device_mpeg_byte_order:
for (nbytes = 0,
ptr = (uint32_t *)sc->enc_pool.bufs[current].vaddr;
nbytes != sc->enc_pool.bufs[current].size;
nbytes += sizeof(*ptr), ptr++)
*ptr = bswap32(*ptr);
break;
case cxm_device_yuv12_byte_order:
scratch = (current + (CXM_SG_BUFFERS - 1)) % CXM_SG_BUFFERS;
if (offset) {
current = scratch;
break;
}
src = (uint32_t *)sc->enc_pool.bufs[current].vaddr;
words_per_line = sc->profile->width / sizeof(*ptr);
macroblocks_per_line
= sc->profile->width / CXM_MACROBLOCK_WIDTH;
strips = sc->enc_pool.bufs[current].size
/ (macroblocks_per_line * CXM_MACROBLOCK_SIZE);
for (i = 0; i < strips; i++) {
ptr = (uint32_t *)sc->enc_pool.bufs[scratch].vaddr
+ i * macroblocks_per_line * CXM_MACROBLOCK_SIZE
/ sizeof(*ptr);
for (j = 0; j < macroblocks_per_line; j++) {
for (k = 0; k < CXM_MACROBLOCK_HEIGHT; k++) {
#if CXM_MACROBLOCK_WIDTH != 16
# error CXM_MACROBLOCK_WIDTH != 16
#endif
*(ptr + k * words_per_line)
= *src++;
*(ptr + k * words_per_line + 1)
= *src++;
*(ptr + k * words_per_line + 2)
= *src++;
*(ptr + k * words_per_line + 3)
= *src++;
}
ptr += CXM_MACROBLOCK_WIDTH / sizeof(*ptr);
}
}
sc->enc_pool.bufs[scratch].size
= sc->enc_pool.bufs[current].size;
current = scratch;
break;
default:
break;
}
sc->enc_pool.bufs[current].byte_order = cxm_host_byte_order;
return current;
}
static void
cxm_encoder_dma_discard(struct cxm_softc *sc)
{
uint32_t parameters[3];
parameters[0] = 0;
parameters[1] = 0;
parameters[2] = 0;
if (cxm_queue_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_SCHED_DMA_TO_HOST,
parameters, 3) == -1) {
device_printf(sc->dev,
"failed to discard encoder dma request\n");
return;
}
sc->encoding_dma = -1;
}
static void
cxm_encoder_dma_done(struct cxm_softc *sc)
{
int buffers_pending;
uint32_t status;
if (!sc->encoding_dma) {
device_printf(sc->dev,
"encoder dma not already in progress\n");
return;
}
buffers_pending = sc->encoding_dma;
sc->encoding_dma = 0;
if (buffers_pending < 0)
return;
status = CSR_READ_4(sc, CXM_REG_DMA_STATUS) & 0x0000000f;
if ((status
& (CXM_DMA_ERROR_LIST | CXM_DMA_ERROR_WRITE | CXM_DMA_SUCCESS))
!= CXM_DMA_SUCCESS) {
device_printf(sc->dev, "encoder dma status %#x\n",
(unsigned int)status);
return;
}
crit_enter();
sc->enc_pool.write = (sc->enc_pool.write + buffers_pending)
% CXM_SG_BUFFERS;
crit_exit();
if (sc->enc_proc)
ksignal (sc->enc_proc, sc->enc_signal);
wakeup(&sc->enc_pool.read);
KNOTE(&sc->enc_kq.ki_note, 0);
}
static void
cxm_encoder_dma_request(struct cxm_softc *sc)
{
enum cxm_byte_order byte_order;
int buffers_free;
int buffers_pending;
unsigned int current;
unsigned int i;
unsigned int mailbox;
unsigned int macroblocks_per_line;
unsigned int nrequests;
unsigned int strips;
uint32_t parameters[CXM_MBX_MAX_PARAMETERS];
uint32_t type;
size_t max_sg_segment;
struct {
size_t offset;
size_t size;
} requests[2];
if (sc->encoding_dma) {
device_printf(sc->dev, "encoder dma already in progress\n");
cxm_encoder_dma_discard(sc);
return;
}
mailbox = sc->enc_mbx
+ CXM_MBX_FW_DMA_MAILBOX * sizeof(struct cxm_mailbox);
for (i = 0; i < CXM_MBX_MAX_PARAMETERS; i++)
parameters[i]
= CSR_READ_4(sc,
mailbox
+ offsetof(struct cxm_mailbox, parameters)
+ i * sizeof(uint32_t)
);
byte_order = cxm_device_mpeg_byte_order;
max_sg_segment = CXM_SG_SEGMENT;
nrequests = 0;
type = parameters[0];
switch (type) {
case 0:
requests[nrequests].offset = parameters[1];
requests[nrequests++].size = parameters[2];
break;
case 1:
byte_order = cxm_device_yuv12_byte_order;
#if CXM_MACROBLOCK_SIZE % 256
# error CXM_MACROBLOCK_SIZE not a multiple of 256
#endif
macroblocks_per_line = sc->profile->width
/ CXM_MACROBLOCK_WIDTH;
strips = CXM_SG_SEGMENT
/ (macroblocks_per_line * CXM_MACROBLOCK_SIZE);
max_sg_segment = strips
* macroblocks_per_line * CXM_MACROBLOCK_SIZE;
requests[nrequests].offset = parameters[1];
requests[nrequests++].size = parameters[2];
requests[nrequests].offset = parameters[3];
requests[nrequests++].size = parameters[4];
break;
case 2:
case 3:
default:
device_printf(sc->dev, "encoder dma type %#x unsupported\n",
(unsigned int)type);
cxm_encoder_dma_discard(sc);
return;
}
crit_enter();
buffers_free = sc->enc_pool.read - sc->enc_pool.write;
if (buffers_free <= 0)
buffers_free += CXM_SG_BUFFERS;
crit_exit();
buffers_pending = 0;
current = sc->enc_pool.write;
for (i = 0; i < nrequests; i++) {
if (!requests[i].size) {
device_printf(sc->dev, "encoder dma size is zero\n");
cxm_encoder_dma_discard(sc);
return;
}
while (requests[i].size) {
sc->enc_pool.bufs[current].size
= requests[i].size > max_sg_segment
? max_sg_segment : requests[i].size;
sc->enc_pool.bufs[current].byte_order = byte_order;
sc->enc_sg.vaddr[buffers_pending].src
= requests[i].offset;
sc->enc_sg.vaddr[buffers_pending].dst
= sc->enc_pool.bufs[current].baddr;
sc->enc_sg.vaddr[buffers_pending].size
= (sc->enc_pool.bufs[current].size + 0x000000ff)
& 0xffffff00;
requests[i].offset += sc->enc_pool.bufs[current].size;
requests[i].size -= sc->enc_pool.bufs[current].size;
buffers_pending++;
current = (current + 1) % CXM_SG_BUFFERS;
if (buffers_pending >= buffers_free) {
device_printf(sc->dev,
"encoder dma not enough buffer space free\n");
cxm_encoder_dma_discard(sc);
return;
}
}
}
sc->enc_sg.vaddr[buffers_pending - 1].size |= 0x80000000;
parameters[0] = sc->enc_sg.baddr;
parameters[1] = buffers_pending * sizeof(sc->enc_sg.vaddr[0]);
parameters[2] = type;
if (cxm_queue_firmware_command(sc, cxm_enc_mailbox,
CXM_FW_CMD_SCHED_DMA_TO_HOST,
parameters, 3) == -1) {
device_printf(sc->dev,
"failed to schedule encoder dma request\n");
return;
}
sc->encoding_dma = buffers_pending;
}
static int
cxm_encoder_wait_for_lock(struct cxm_softc *sc)
{
int muted;
int locked;
int result;
locked = 1;
if (sc->source == cxm_fm_source || sc->source == cxm_tuner_source) {
result = cxm_tuner_wait_for_lock(sc);
if (result <= 0)
return result;
}
if (sc->source != cxm_fm_source) {
result = cxm_saa7115_wait_for_lock(sc);
if (result < 0)
return result;
else if (result == 0)
locked = 0;
}
if (sc->source == cxm_tuner_source) {
muted = cxm_msp_is_muted(sc);
result = cxm_msp_autodetect_standard(sc);
if (result < 0)
return result;
else if (result == 0)
locked = 0;
if (muted == 0 && cxm_msp_unmute(sc) < 0)
return -1;
}
return locked;
}
static void
cxm_mapmem(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
bus_addr_t *busaddrp;
busaddrp = (bus_addr_t *)arg;
*busaddrp = segs->ds_addr;
}
static int
cxm_probe(device_t dev)
{
struct cxm_dev *t;
t = cxm_devs;
while(t->name != NULL) {
if ((pci_get_vendor(dev) == t->vid) &&
(pci_get_device(dev) == t->did)) {
device_set_desc(dev, t->name);
return 0;
}
t++;
}
return ENXIO;
}
static int
cxm_attach(device_t dev)
{
int error;
int rid;
int unit;
unsigned int i;
uint32_t command;
struct cxm_softc *sc;
sc = device_get_softc(dev);
unit = device_get_unit(dev);
sc->dev = dev;
sc->type = cxm_iTVC15_type;
switch(pci_get_device(dev)) {
case PCI_PRODUCT_ICOMPRESSION_ITVC16:
sc->type = cxm_iTVC16_type;
break;
default:
break;
}
pci_enable_busmaster(dev);
pci_enable_io(dev, SYS_RES_MEMORY);
command = pci_read_config(dev, PCIR_COMMAND, 4);
if (!(command & PCIM_CMD_MEMEN)) {
device_printf(dev, "failed to enable memory mappings\n");
error = ENXIO;
goto fail;
}
rid = CXM_RID;
sc->mem_res = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
0, ~0, 1, RF_ACTIVE);
if (!sc->mem_res) {
device_printf(dev, "could not map memory\n");
error = ENXIO;
goto fail;
}
sc->btag = rman_get_bustag(sc->mem_res);
sc->bhandle = rman_get_bushandle(sc->mem_res);
sc->cxm_iic = device_add_child(dev, "cxm_iic", unit);
if (!sc->cxm_iic) {
device_printf(dev, "could not add cxm_iic\n");
error = ENXIO;
goto fail;
}
error = device_probe_and_attach(sc->cxm_iic);
if (error) {
device_printf(dev, "could not attach cxm_iic\n");
goto fail;
}
if (cxm_tuner_init(sc) < 0) {
device_printf(dev, "could not initialize tuner\n");
error = ENXIO;
goto fail;
}
if (cxm_saa7115_init(sc) < 0) {
device_printf(dev, "could not initialize video decoder\n");
error = ENXIO;
goto fail;
}
if (cxm_msp_init(sc) < 0) {
device_printf(dev, "could not initialize audio decoder\n");
error = ENXIO;
goto fail;
}
if (cxm_ir_init(sc) < 0) {
device_printf(dev, "could not initialize IR remote\n");
error = ENXIO;
goto fail;
}
sc->dec_mbx = -1;
sc->enc_mbx = -1;
cxm_stop_hardware(sc);
rid = 0;
sc->irq_res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid,
0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
if (sc->irq_res == NULL) {
device_printf(dev, "could not map interrupt\n");
error = ENXIO;
goto fail;
}
error = bus_setup_intr(dev, sc->irq_res, 0,
cxm_intr, sc, &sc->ih_cookie, NULL);
if (error) {
device_printf(dev, "could not setup irq\n");
goto fail;
}
error = bus_dma_tag_create(NULL, 1, 0,
BUS_SPACE_MAXADDR_32BIT,
BUS_SPACE_MAXADDR,
BUS_SPACE_MAXSIZE_32BIT, 1,
BUS_SPACE_MAXSIZE_32BIT, 0,
&sc->parent_dmat);
if (error) {
device_printf(dev, "could not create parent bus DMA tag\n");
goto fail;
}
error = bus_dma_tag_create(sc->parent_dmat, 256, 0,
BUS_SPACE_MAXADDR_32BIT,
BUS_SPACE_MAXADDR,
CXM_SG_SEGMENT, 1,
BUS_SPACE_MAXSIZE_32BIT, 0,
&sc->enc_pool.dmat);
if (error) {
device_printf(dev,
"could not create encoder buffer DMA tag\n");
goto fail;
}
for (i = 0; i < CXM_SG_BUFFERS; i++) {
error = bus_dmamem_alloc(sc->enc_pool.dmat,
(void **)&sc->enc_pool.bufs[i].vaddr,
BUS_DMA_NOWAIT,
&sc->enc_pool.bufs[i].dmamap);
if (error) {
device_printf(dev,
"could not allocate encoder buffer\n");
goto fail;
}
error = bus_dmamap_load(sc->enc_pool.dmat,
sc->enc_pool.bufs[i].dmamap,
sc->enc_pool.bufs[i].vaddr,
CXM_SG_SEGMENT,
cxm_mapmem,
&sc->enc_pool.bufs[i].baddr, 0);
if (error) {
device_printf(dev, "could not map encoder buffer\n");
goto fail;
}
}
error = bus_dma_tag_create(sc->parent_dmat, 1, 0,
BUS_SPACE_MAXADDR_32BIT,
BUS_SPACE_MAXADDR,
CXM_SG_BUFFERS
* sizeof(struct cxm_sg_entry), 1,
BUS_SPACE_MAXSIZE_32BIT, 0,
&sc->enc_sg.dmat);
if (error) {
device_printf(dev,
"could not create scatter / gather DMA tag\n");
goto fail;
}
error = bus_dmamem_alloc(sc->enc_sg.dmat, (void **)&sc->enc_sg.vaddr,
BUS_DMA_NOWAIT, &sc->enc_sg.dmamap);
if (error) {
device_printf(dev,
"could not allocate scatter / gather list\n");
goto fail;
}
error = bus_dmamap_load(sc->enc_sg.dmat, sc->enc_sg.dmamap,
sc->enc_sg.vaddr,
CXM_SG_BUFFERS * sizeof(struct cxm_sg_entry),
cxm_mapmem, &sc->enc_sg.baddr, 0);
if (error) {
device_printf(dev, "could not map scatter / gather list\n");
goto fail;
}
if (cxm_init_hardware(sc) < 0) {
device_printf(dev, "could not initialize hardware\n");
error = ENXIO;
goto fail;
}
sc->profile = &dvd_full_d1_ntsc_profile;
sc->source = cxm_tuner_source;
sc->cxm_dev_t = make_dev(&cxm_ops, unit,
0, 0, 0444, "cxm%d", unit);
return 0;
fail:
if (sc->enc_sg.baddr)
bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap);
if (sc->enc_sg.vaddr)
bus_dmamem_free(sc->enc_sg.dmat, sc->enc_sg.vaddr,
sc->enc_sg.dmamap);
if (sc->enc_sg.dmat)
bus_dma_tag_destroy(sc->enc_sg.dmat);
for (i = 0; i < CXM_SG_BUFFERS; i++) {
if (sc->enc_pool.bufs[i].baddr)
bus_dmamap_unload(sc->enc_pool.dmat,
sc->enc_pool.bufs[i].dmamap);
if (sc->enc_pool.bufs[i].vaddr)
bus_dmamem_free(sc->enc_pool.dmat,
sc->enc_pool.bufs[i].vaddr,
sc->enc_pool.bufs[i].dmamap);
}
if (sc->enc_pool.dmat)
bus_dma_tag_destroy(sc->enc_pool.dmat);
if (sc->parent_dmat)
bus_dma_tag_destroy(sc->parent_dmat);
bus_generic_detach(dev);
if (sc->cxm_iic)
device_delete_child(dev, sc->cxm_iic);
if (sc->ih_cookie)
bus_teardown_intr(dev, sc->irq_res, sc->ih_cookie);
if (sc->irq_res)
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
if (sc->mem_res)
bus_release_resource(dev, SYS_RES_MEMORY, CXM_RID, sc->mem_res);
return error;
}
static int
cxm_detach(device_t dev)
{
unsigned int i;
struct cxm_softc *sc;
device_t child;
sc = device_get_softc(dev);
cxm_stop_hardware(sc);
dev_ops_remove_minor(&cxm_ops, device_get_unit(dev));
bus_dmamap_unload(sc->enc_sg.dmat, sc->enc_sg.dmamap);
bus_dmamem_free(sc->enc_sg.dmat, sc->enc_sg.vaddr, sc->enc_sg.dmamap);
bus_dma_tag_destroy(sc->enc_sg.dmat);
for (i = 0; i < CXM_SG_BUFFERS; i++) {
bus_dmamap_unload(sc->enc_pool.dmat,
sc->enc_pool.bufs[i].dmamap);
bus_dmamem_free(sc->enc_pool.dmat, sc->enc_pool.bufs[i].vaddr,
sc->enc_pool.bufs[i].dmamap);
}
bus_dma_tag_destroy(sc->enc_pool.dmat);
bus_dma_tag_destroy(sc->parent_dmat);
child = sc->cxm_iic;
bus_generic_detach(dev);
if (child)
device_delete_child(dev, child);
bus_teardown_intr(dev, sc->irq_res, sc->ih_cookie);
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
bus_release_resource(dev, SYS_RES_MEMORY, CXM_RID, sc->mem_res);
return 0;
}
static int
cxm_shutdown(device_t dev)
{
struct cxm_softc *sc = device_get_softc(dev);
cxm_stop_hardware(sc);
return 0;
}
static void
cxm_intr(void *arg)
{
uint32_t status;
struct cxm_softc *sc;
sc = (struct cxm_softc *)arg;
status = CSR_READ_4(sc, CXM_REG_IRQ_STATUS);
status &= ~sc->irq_mask;
if (!status)
return;
if (status & CXM_IRQ_ENC_DMA_DONE)
cxm_encoder_dma_done(sc);
if (status & CXM_IRQ_ENC_DMA_REQUEST)
cxm_encoder_dma_request(sc);
if (status & CXM_IRQ_ENC_EOS) {
sc->encoding_eos = 1;
wakeup(&sc->encoding_eos);
}
cxm_set_irq_status(sc, status);
}
static void
cxm_child_detached(device_t dev, device_t child)
{
struct cxm_softc *sc;
sc = device_get_softc(dev);
if (child == sc->cxm_iic)
sc->cxm_iic = NULL;
}
static int
cxm_read_ivar(device_t dev, device_t child, int index, uintptr_t* val)
{
struct cxm_softc *sc;
sc = device_get_softc(dev);
switch (index) {
case CXM_IVAR_BHANDLE:
*(bus_space_handle_t **)val = &sc->bhandle;
break;
case CXM_IVAR_BTAG:
*(bus_space_tag_t **)val = &sc->btag;
break;
case CXM_IVAR_IICBUS:
*(device_t **)val = &sc->iicbus;
break;
default:
return ENOENT;
}
return 0;
}
static int
cxm_write_ivar(device_t dev, device_t child, int index, uintptr_t val)
{
struct cxm_softc *sc;
sc = device_get_softc(dev);
switch (index) {
case CXM_IVAR_BHANDLE:
return EINVAL;
case CXM_IVAR_BTAG:
return EINVAL;
case CXM_IVAR_IICBUS:
if (sc->iicbus)
return EINVAL;
sc->iicbus = val ? *(device_t *)val : NULL;
break;
default:
return ENOENT;
}
return 0;
}
#define UNIT(x) ((x) & 0x0f)
#define FUNCTION(x) (x >> 4)
static int
cxm_open(struct dev_open_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
int unit;
struct cxm_softc *sc;
unit = UNIT(minor(dev));
sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
if (sc == NULL) {
return ENXIO;
}
if (sc->is_opened)
return EBUSY;
sc->is_opened = 1;
sc->mpeg = 1;
device_busy(devclass_get_device(cxm_devclass, unit));
return 0;
}
static int
cxm_close(struct dev_close_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
int unit;
struct cxm_softc *sc;
unit = UNIT(minor(dev));
sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
if (sc == NULL) {
return ENXIO;
}
if (cxm_stop_encoder(sc) < 0)
return ENXIO;
sc->enc_pool.offset = 0;
sc->enc_pool.read = 0;
sc->enc_pool.write = 0;
sc->enc_proc = NULL;
sc->enc_signal = 0;
device_unbusy(devclass_get_device(cxm_devclass, unit));
sc->is_opened = 0;
return 0;
}
static int
cxm_read(struct dev_read_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
int buffers_available;
int buffers_read;
int error;
int unit;
unsigned int current;
unsigned int i;
size_t nbytes;
size_t offset;
struct cxm_softc *sc;
unit = UNIT(minor(dev));
sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
if (sc == NULL) {
return ENXIO;
}
if (!sc->encoding && sc->enc_pool.read == sc->enc_pool.write) {
if (cxm_start_encoder(sc) < 0)
return ENXIO;
if (ap->a_ioflag & IO_NDELAY)
return EWOULDBLOCK;
}
buffers_available = 0;
crit_enter();
while (sc->enc_pool.read == sc->enc_pool.write) {
error = tsleep(&sc->enc_pool.read, PCATCH, "cxmrd", 0);
if (error) {
crit_exit();
return error;
}
}
buffers_available = sc->enc_pool.write - sc->enc_pool.read;
if (buffers_available < 0)
buffers_available += CXM_SG_BUFFERS;
crit_exit();
offset = sc->enc_pool.offset;
for (buffers_read = 0, i = sc->enc_pool.read;
buffers_read != buffers_available && ap->a_uio->uio_resid;
buffers_read++, i = (i + 1) % CXM_SG_BUFFERS) {
current = cxm_encoder_fixup_byte_order (sc, i, offset);
nbytes = sc->enc_pool.bufs[current].size - offset;
if (nbytes > ap->a_uio->uio_resid)
nbytes = ap->a_uio->uio_resid;
error = uiomove(sc->enc_pool.bufs[current].vaddr + offset,
nbytes, ap->a_uio);
if (error)
return error;
offset += nbytes;
if (!ap->a_uio->uio_resid && offset != sc->enc_pool.bufs[i].size)
break;
offset = 0;
}
sc->enc_pool.offset = offset;
crit_enter();
sc->enc_pool.read = (sc->enc_pool.read + buffers_read)
% CXM_SG_BUFFERS;
crit_exit();
return 0;
}
static int
cxm_ioctl(struct dev_ioctl_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
int brightness;
int chroma_saturation;
int contrast;
int fps;
int hue;
int result;
int status;
int unit;
unsigned int i;
unsigned int sig;
unsigned long freq;
struct cxm_softc *sc;
enum cxm_source source;
struct bktr_capture_area *cap;
struct bktr_remote *remote;
unit = UNIT(minor(dev));
sc = (struct cxm_softc*)devclass_get_softc(cxm_devclass, unit);
if (sc == NULL) {
return ENXIO;
}
switch (ap->a_cmd) {
case BT848_GAUDIO:
switch (cxm_msp_selected_source(sc)) {
case cxm_tuner_source:
*(int *) ap->a_data = AUDIO_TUNER;
break;
case cxm_line_in_source_composite:
case cxm_line_in_source_svideo:
*(int *) ap->a_data = AUDIO_EXTERN;
break;
case cxm_fm_source:
*(int *) ap->a_data = AUDIO_INTERN;
break;
default:
return ENXIO;
}
if (cxm_msp_is_muted(sc) == 1)
*(int *) ap->a_data |= AUDIO_MUTE;
break;
case BT848_SAUDIO:
source = cxm_unknown_source;
switch (*(int *) ap->a_data) {
case AUDIO_TUNER:
source = cxm_tuner_source;
break;
case AUDIO_EXTERN:
source = cxm_line_in_source_composite;
break;
case AUDIO_INTERN:
source = cxm_fm_source;
break;
case AUDIO_MUTE:
if (cxm_msp_mute(sc) < 0)
return ENXIO;
return 0;
case AUDIO_UNMUTE:
if (cxm_msp_unmute(sc) < 0)
return ENXIO;
return 0;
default:
return EINVAL;
}
if (sc->encoding) {
if (source != sc->source
&& (source == cxm_fm_source
|| sc->source == cxm_fm_source))
return EBUSY;
}
if (cxm_pause_encoder(sc) < 0)
return ENXIO;
if (cxm_msp_select_source(sc, source) < 0)
return ENXIO;
if (source == cxm_fm_source)
sc->source = source;
result = cxm_encoder_wait_for_lock(sc);
if (result < 0)
return ENXIO;
else if (result == 0)
return EINVAL;
if (cxm_unpause_encoder(sc) < 0)
return ENXIO;
break;
case BT848_GBRIG:
brightness = cxm_saa7115_get_brightness(sc);
if (brightness < 0)
return ENXIO;
*(int *)ap->a_data = (int)(unsigned char)brightness - 128;
break;
case BT848_SBRIG:
brightness = *(int *)ap->a_data + 128;
if (cxm_saa7115_set_brightness(sc, brightness) < 0)
return ENXIO;
break;
case METEORGBRIG:
brightness = cxm_saa7115_get_brightness(sc);
if (brightness < 0)
return ENXIO;
*(unsigned char *)ap->a_data = (unsigned char)brightness;
break;
case METEORSBRIG:
brightness = *(unsigned char *)ap->a_data;
if (cxm_saa7115_set_brightness(sc, brightness) < 0)
return ENXIO;
break;
case BT848_GCSAT:
chroma_saturation = cxm_saa7115_get_chroma_saturation(sc);
if (chroma_saturation < 0)
return ENXIO;
*(int *)ap->a_data = ((signed char)chroma_saturation > 0)
? (chroma_saturation * 4 - 2) : 0;
break;
case BT848_SCSAT:
chroma_saturation = (*(int *)ap->a_data & 0x1ff) < 510
? ((*(int *)ap->a_data & 0x1ff) + 2) / 4 : 127;
if (cxm_saa7115_set_chroma_saturation(sc, chroma_saturation)
< 0)
return ENXIO;
break;
case METEORGCSAT:
chroma_saturation = cxm_saa7115_get_chroma_saturation(sc);
if (chroma_saturation < 0)
return ENXIO;
*(unsigned char *)ap->a_data = (unsigned char)chroma_saturation;
break;
case METEORSCSAT:
chroma_saturation = *(unsigned char *)ap->a_data;
if (cxm_saa7115_set_chroma_saturation(sc, chroma_saturation)
< 0)
return ENXIO;
break;
case METEORGCONT:
contrast = cxm_saa7115_get_contrast(sc);
if (contrast < 0)
return ENXIO;
*(unsigned char *)ap->a_data = (unsigned char)contrast;
break;
case METEORSCONT:
contrast = *(unsigned char *)ap->a_data;
if (cxm_saa7115_set_contrast(sc, contrast) < 0)
return ENXIO;
break;
case BT848_GHUE:
hue = cxm_saa7115_get_hue(sc);
if (hue < 0)
return ENXIO;
*(int *)ap->a_data = (signed char)hue;
break;
case BT848_SHUE:
hue = *(int *)ap->a_data;
if (cxm_saa7115_set_hue(sc, hue) < 0)
return ENXIO;
break;
case METEORGHUE:
hue = cxm_saa7115_get_hue(sc);
if (hue < 0)
return ENXIO;
*(signed char *)ap->a_data = (signed char)hue;
break;
case METEORSHUE:
hue = *(signed char *)ap->a_data;
if (cxm_saa7115_set_hue(sc, hue) < 0)
return ENXIO;
break;
case METEORCAPTUR:
switch (*(int *) ap->a_data) {
case METEOR_CAP_CONTINOUS:
if (cxm_start_encoder(sc) < 0)
return ENXIO;
break;
case METEOR_CAP_STOP_CONT:
if (cxm_stop_encoder(sc) < 0)
return ENXIO;
break;
default:
return EINVAL;
}
break;
case BT848_GCAPAREA:
cap = (struct bktr_capture_area *)ap->a_data;
memset (cap, 0, sizeof (*cap));
cap->x_offset = 0;
cap->y_offset = 0;
cap->x_size = sc->profile->width;
cap->y_size = sc->profile->height;
break;
case BT848_SCAPAREA:
if (sc->encoding)
return EBUSY;
cap = (struct bktr_capture_area *)ap->a_data;
if (cap->x_offset || cap->y_offset
|| (cap->x_size % CXM_MACROBLOCK_WIDTH)
|| (cap->y_size % CXM_MACROBLOCK_HEIGHT))
return EINVAL;
for (i = 0; i < NUM_ELEMENTS(codec_profiles); i++)
if (codec_profiles[i]->width == cap->x_size
&& codec_profiles[i]->height == cap->y_size)
break;
if (i >= NUM_ELEMENTS(codec_profiles))
return EINVAL;
sc->profile = codec_profiles[i];
break;
case BT848GFMT:
switch (cxm_saa7115_detected_format(sc)) {
case cxm_ntsc_60hz_source_format:
*(unsigned long *)ap->a_data = BT848_IFORM_F_NTSCM;
break;
case cxm_pal_50hz_source_format:
*(unsigned long *)ap->a_data = BT848_IFORM_F_PALBDGHI;
break;
case cxm_secam_50hz_source_format:
*(unsigned long *)ap->a_data = BT848_IFORM_F_SECAM;
break;
case cxm_pal_60hz_source_format:
*(unsigned long *)ap->a_data = BT848_IFORM_F_PALM;
break;
case cxm_bw_50hz_source_format:
case cxm_bw_60hz_source_format:
case cxm_ntsc_50hz_source_format:
*(unsigned long *)ap->a_data = BT848_IFORM_F_AUTO;
break;
default:
return ENXIO;
}
break;
case METEORGFMT:
switch (cxm_saa7115_detected_format(sc)) {
case cxm_ntsc_60hz_source_format:
*(unsigned long *)ap->a_data = METEOR_FMT_NTSC;
break;
case cxm_pal_50hz_source_format:
*(unsigned long *)ap->a_data = METEOR_FMT_PAL;
break;
case cxm_secam_50hz_source_format:
*(unsigned long *)ap->a_data = METEOR_FMT_SECAM;
break;
case cxm_bw_50hz_source_format:
case cxm_bw_60hz_source_format:
case cxm_ntsc_50hz_source_format:
case cxm_pal_60hz_source_format:
*(unsigned long *)ap->a_data = METEOR_FMT_AUTOMODE;
break;
default:
return ENXIO;
}
break;
case METEORGFPS:
fps = cxm_saa7115_detected_fps(sc);
if (fps < 0)
return ENXIO;
*(unsigned short *)ap->a_data = fps;
break;
case METEORGINPUT:
switch (sc->source) {
case cxm_tuner_source:
*(unsigned long *)ap->a_data = METEOR_INPUT_DEV1;
break;
case cxm_line_in_source_composite:
*(unsigned long *)ap->a_data = METEOR_INPUT_DEV2;
break;
case cxm_line_in_source_svideo:
*(unsigned long *)ap->a_data = METEOR_INPUT_DEV_SVIDEO;
break;
default:
return ENXIO;
}
break;
case METEORSINPUT:
source = cxm_unknown_source;
switch (*(unsigned long *)ap->a_data & 0xf000) {
case METEOR_INPUT_DEV1:
source = cxm_tuner_source;
break;
case METEOR_INPUT_DEV2:
source = cxm_line_in_source_composite;
break;
case METEOR_INPUT_DEV_SVIDEO:
source = cxm_line_in_source_svideo;
break;
default:
return EINVAL;
}
if (sc->encoding) {
if (source != sc->source
&& (source == cxm_fm_source
|| sc->source == cxm_fm_source))
return EBUSY;
}
if (cxm_pause_encoder(sc) < 0)
return ENXIO;
if (cxm_saa7115_select_source(sc, source) < 0)
return ENXIO;
if (cxm_msp_select_source(sc, source) < 0)
return ENXIO;
sc->source = source;
result = cxm_encoder_wait_for_lock(sc);
if (result < 0)
return ENXIO;
else if (result == 0)
return EINVAL;
if (cxm_unpause_encoder(sc) < 0)
return ENXIO;
break;
case METEORGSIGNAL:
*(unsigned int *)ap->a_data = sc->enc_signal;
break;
case METEORSSIGNAL:
sig = *(unsigned int *)ap->a_data;
if (!_SIG_VALID(sig))
return EINVAL;
if (sig == METEOR_SIG_MODE_MASK)
sig = 0;
crit_enter();
sc->enc_proc = sig ? curproc : NULL;
sc->enc_signal = sig;
crit_exit();
break;
case RADIO_GETFREQ:
freq = sc->tuner_freq / 10;
*(unsigned int *)ap->a_data = freq;
break;
case RADIO_SETFREQ:
if (sc->source == cxm_fm_source)
if (cxm_pause_encoder(sc) < 0)
return ENXIO;
freq = *(unsigned int *)ap->a_data * 10;
if (cxm_tuner_select_frequency(sc, cxm_tuner_fm_freq_type,
freq) < 0)
return ENXIO;
if (cxm_tuner_wait_for_lock(sc) < 0)
return EINVAL;
result = cxm_encoder_wait_for_lock(sc);
if (result < 0)
return ENXIO;
else if (result == 0)
return EINVAL;
if (sc->source == cxm_fm_source)
if (cxm_unpause_encoder(sc) < 0)
return ENXIO;
break;
case TVTUNER_GETAFC:
*(int *)ap->a_data = sc->tuner_afc;
break;
case TVTUNER_SETAFC:
sc->tuner_afc = (*(int *)ap->a_data != 0);
break;
case TVTUNER_GETTYPE:
*(unsigned int *)ap->a_data = cxm_tuner_selected_channel_set(sc);
break;
case TVTUNER_SETTYPE:
if (cxm_tuner_select_channel_set(sc, *(unsigned int *)ap->a_data) < 0)
return EINVAL;
break;
case TVTUNER_SETCHNL:
if (sc->source == cxm_tuner_source)
if (cxm_pause_encoder(sc) < 0)
return ENXIO;
if (cxm_tuner_select_channel(sc, *(unsigned int *)ap->a_data) < 0)
return ENXIO;
if (sc->tuner_afc)
if (cxm_tuner_apply_afc(sc) < 0)
return EINVAL;
if (cxm_tuner_wait_for_lock(sc) < 0)
return EINVAL;
result = cxm_encoder_wait_for_lock(sc);
if (result < 0)
return ENXIO;
else if (result == 0)
return EINVAL;
if (sc->source == cxm_tuner_source)
if (cxm_unpause_encoder(sc) < 0)
return ENXIO;
break;
case TVTUNER_GETFREQ:
freq = (sc->tuner_freq * 16) / 1000;
*(unsigned int *)ap->a_data = freq;
break;
case TVTUNER_SETFREQ:
if (sc->source == cxm_tuner_source)
if (cxm_pause_encoder(sc) < 0)
return ENXIO;
freq = (*(unsigned int *)ap->a_data * 1000) / 16;
if (cxm_tuner_select_frequency(sc, cxm_tuner_tv_freq_type,
freq) < 0)
return ENXIO;
if (cxm_tuner_wait_for_lock(sc) < 0)
return EINVAL;
result = cxm_encoder_wait_for_lock(sc);
if (result < 0)
return ENXIO;
else if (result == 0)
return EINVAL;
if (sc->source == cxm_tuner_source)
if (cxm_unpause_encoder(sc) < 0)
return ENXIO;
break;
case TVTUNER_GETSTATUS:
status = cxm_tuner_status(sc);
if (status < 0)
return ENXIO;
*(unsigned long *)ap->a_data = status & 0xff;
break;
case REMOTE_GETKEY:
remote = (struct bktr_remote *)ap->a_data;
if (cxm_ir_key(sc, (char *)remote, sizeof(*remote)) < 0)
return ENXIO;
break;
default:
return ENOTTY;
}
return 0;
}
static struct filterops cxm_filterops =
{ FILTEROP_ISFD, NULL, cxm_filter_detach, cxm_filter };
static int
cxm_kqfilter(struct dev_kqfilter_args *ap)
{
cdev_t dev = ap->a_head.a_dev;
struct knote *kn = ap->a_kn;
struct cxm_softc *sc;
struct klist *klist;
int unit;
ap->a_result = 0;
switch (kn->kn_filter) {
case EVFILT_READ:
unit = UNIT(minor(dev));
sc = (struct cxm_softc *)devclass_get_softc(cxm_devclass, unit);
kn->kn_fop = &cxm_filterops;
kn->kn_hook = (caddr_t)sc;
break;
default:
ap->a_result = EOPNOTSUPP;
return (0);
}
klist = &sc->enc_kq.ki_note;
knote_insert(klist, kn);
return (0);
}
static void
cxm_filter_detach(struct knote *kn)
{
struct cxm_softc *sc = (struct cxm_softc *)kn->kn_hook;
struct klist *klist = &sc->enc_kq.ki_note;
knote_remove(klist, kn);
}
static int
cxm_filter(struct knote *kn, long hint)
{
struct cxm_softc *sc = (struct cxm_softc *)kn->kn_hook;
int ready = 0;
if (sc == NULL) {
kn->kn_flags |= (EV_EOF | EV_NODATA);
return (1);
}
crit_enter();
if (sc->enc_pool.read != sc->enc_pool.write)
ready = 1;
crit_exit();
return (ready);
}