diff options
Diffstat (limited to 'linux')
16 files changed, 740 insertions, 76 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c new file mode 100644 index 000000000..df1eeefd5 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c @@ -0,0 +1,276 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + + This source file is specifically designed to interface with the + cx2584x, in kernels 2.6.16 or newer. + +*/ + +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-video-v4l.h" +#include "pvrusb2-i2c-cmd-v4l2.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include <cx25840.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <linux/errno.h> +#include <linux/slab.h> + +struct pvr2_v4l_cx2584x { + struct pvr2_i2c_handler handler; + struct pvr2_decoder_ctrl ctrl; + struct pvr2_i2c_client *client; + struct pvr2_hdw *hdw; + unsigned long stale_mask; +}; + + +static void set_input(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + struct v4l2_audio inp; + int msk = 0; + + int v = 0; + + switch(hdw->controls[PVR2_CID_INPUT].value) { + case PVR2_CVAL_INPUT_TV: + msk = (8 << 16) | 7; + break; + case PVR2_CVAL_INPUT_COMPOSITE: + msk = (0 << 16) | 3; + break; + case PVR2_CVAL_INPUT_SVIDEO: + msk = (0 << 16) | 0x510; + break; + case PVR2_CVAL_INPUT_RADIO: + msk = 0; /* FIXME: Need to figure out radio */ + break; + default: + break; + } + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input(val=%d msk=0x%x)", + hdw->controls[PVR2_CID_INPUT].value,msk); + + v = msk & 0x0000ffffu; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x S_INPUT val=0x%x",v); + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_INPUT,&v); + v = (msk >> 16) & 0x0000ffffu; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x S_AUDIO val=0x%x",v); + memset(&inp,0,sizeof(inp)); + inp.index = v; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_AUDIO,&inp); +} + + +static int check_input(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_INPUT].dirty != 0; +} + + +static void set_audio(struct pvr2_v4l_cx2584x *ctxt) +{ + u32 val; + struct pvr2_hdw *hdw = ctxt->hdw; + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d", + hdw->controls[PVR2_CID_SRATE].value); + switch (hdw->controls[PVR2_CID_SRATE].value) { + default: + case PVR2_CVAL_SRATE_48: + val = 48000; + break; + case PVR2_CVAL_SRATE_44_1: + val = 44100; + break; + } + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val); +} + + +static int check_audio(struct pvr2_v4l_cx2584x *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_SRATE].dirty != 0; +} + + +struct pvr2_v4l_cx2584x_ops { + void (*update)(struct pvr2_v4l_cx2584x *); + int (*check)(struct pvr2_v4l_cx2584x *); +}; + + +static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = { + { .update = set_input, .check = check_input}, + { .update = set_audio, .check = check_audio}, +}; + + +static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt) +{ + ctxt->client->handler = 0; + ctxt->hdw->decoder_ctrl = 0; + kfree(ctxt); +} + + +static int decoder_check(struct pvr2_v4l_cx2584x *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); + idx++) { + msk = 1 << idx; + if (ctxt->stale_mask & msk) continue; + if (decoder_ops[idx].check(ctxt)) { + ctxt->stale_mask |= msk; + } + } + return ctxt->stale_mask != 0; +} + + +static void decoder_update(struct pvr2_v4l_cx2584x *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]); + idx++) { + msk = 1 << idx; + if (!(ctxt->stale_mask & msk)) continue; + ctxt->stale_mask &= ~msk; + decoder_ops[idx].update(ctxt); + } +} + + +static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl) +{ + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl); + pvr2_v4l2_cmd_stream(ctxt->client,fl); +} + + +static int decoder_detect(struct pvr2_i2c_client *cp) +{ + int ret; + /* Attempt to query the decoder - let's see if it will answer */ + struct v4l2_queryctrl qc; + + memset(&qc,0,sizeof(qc)); + + qc.id = V4L2_CID_BRIGHTNESS; + + ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc); + return ret == 0; /* Return true if it answered */ +} + + +static int decoder_is_tuned(struct pvr2_v4l_cx2584x *ctxt) +{ + struct v4l2_tuner vt; + int ret; + + memset(&vt,0,sizeof(vt)); + ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt); + if (ret < 0) return -EINVAL; + return vt.signal ? 1 : 0; +} + + +static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt, + char *buf,unsigned int cnt) +{ + return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l"); +} + + +static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt) +{ + int ret; + ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,0); + pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret); +} + + +const static struct pvr2_i2c_handler_functions hfuncs = { + .detach = (void (*)(void *))decoder_detach, + .check = (int (*)(void *))decoder_check, + .update = (void (*)(void *))decoder_update, + .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe, +}; + + +int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw, + struct pvr2_i2c_client *cp) +{ + struct pvr2_v4l_cx2584x *ctxt; + + if (hdw->decoder_ctrl) return 0; + if (cp->handler) return 0; + if (!decoder_detect(cp)) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->handler.func_data = ctxt; + ctxt->handler.func_table = &hfuncs; + ctxt->ctrl.ctxt = ctxt; + ctxt->ctrl.detach = (void (*)(void *))decoder_detach; + ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable; + ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned; + ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset; + ctxt->client = cp; + ctxt->hdw = hdw; + ctxt->stale_mask = (1 << (sizeof(decoder_ops)/ + sizeof(decoder_ops[0]))) - 1; + hdw->decoder_ctrl = &ctxt->ctrl; + cp->handler = &ctxt->handler; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up", + cp->client->addr); + return !0; +} + + + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h new file mode 100644 index 000000000..5dea8d7b3 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h @@ -0,0 +1,54 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_CX2584X_V4L_H +#define __PVRUSB2_CX2584X_V4L_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which handles combined device audio & video processing. + This interface is used internally by the driver; higher level code + should only interact through the interface provided by + pvrusb2-hdw.h. + +*/ + +#include "compat.h" + + +#include "pvrusb2-i2c-core.h" + +int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); + + +#endif /* __PVRUSB2_CX2584X_V4L_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index bcfe468eb..16e6ea317 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -362,11 +362,13 @@ int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, } else if (debugifc_match_keyword(wptr,wlen,"bus")) { pvr2_hdw_device_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"soft")) { - return pvr2_hdw_cmd_soft_reset(hdw); + return pvr2_hdw_cmd_powerup(hdw); } else if (debugifc_match_keyword(wptr,wlen,"deep")) { return pvr2_hdw_cmd_deep_reset(hdw); } else if (debugifc_match_keyword(wptr,wlen,"firmware")) { return pvr2_upload_firmware2(hdw); + } else if (debugifc_match_keyword(wptr,wlen,"decoder")) { + return pvr2_hdw_cmd_decoder_reset(hdw); } return -EINVAL; } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) { diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 97931d3c4..553bd2d7b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -294,24 +294,25 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw) /* set stream output port. */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_OUTPUT_PORT, 2, - 0x01, 0x01); + 0x01, 0x02); /* set the Program Index Information. We want I,P,B frames (max 400) */ ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_SET_PGM_INDEX_INFO, 2, 0x07, 0x0190); - /* NOTE : windows driver sends some 0xdc */ - - /* Mike Isely <isely@pobox.com> 19-Jun-2005 I've confirmed - that the Windows driver seems to issue these commands, but - right now I have no idea what these do (and neither does - the ivtv driver). But, if I leave them in, then mplayer - goes nuts with xrun errors. So for now we don't do this. - It sure would be nice to know what these are for. */ -#if 0 - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 1, 5); - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 2, 3, 1); - ret |= pvr2_write_encoder_vcmd(hdw, 0xdc, 1, 8); + /* NOTE : windows driver sends these */ + /* Mike Isely <isely@pobox.com> 7-Mar-2006 The windows driver + sends the following commands but if we do the same then + many apps are no longer able to read the video stream. + Leaving these out seems to do no harm at all, so they're + commented out for that reason. */ +#ifdef notdef + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 5,0,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 3,1,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 8,0,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 4,1,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4, 0,3,0,0); + ret |= pvr2_write_encoder_vcmd(hdw, CX2341X_ENC_MISC,4,15,0,0,0); #endif /* Strange compared to ivtv data. */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 1b161773b..05e44385b 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -74,6 +74,7 @@ struct pvr2_decoder_ctrl { void (*detach)(void *); void (*enable)(void *,int); int (*tuned)(void *); + void (*force_reset)(void *); }; #define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 539456b44..ec2b5eeae 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -618,7 +618,6 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw) ret = 0; /* First prepare firmware loading */ - ret |= pvr2_hdw_cmd_soft_reset(hdw); ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/ @@ -1094,7 +1093,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) } if (!pvr2_hdw_dev_ok(hdw)) return; - pvr2_i2c_core_init(hdw); + pvr2_hdw_cmd_powerup(hdw); if (!pvr2_hdw_dev_ok(hdw)) return; if (pvr2_upload_firmware2(hdw)){ @@ -1103,6 +1102,10 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw) return; } + // This step MUST happen after the earlier powerup step. + pvr2_i2c_core_init(hdw); + if (!pvr2_hdw_dev_ok(hdw)) return; + for (idx = 0; idx < PVR2_CID_COUNT; idx++) { if (control_defs[idx].skip_init) continue; pvr2_hdw_set_ctl_value_internal( @@ -1488,11 +1491,15 @@ int pvr2_hdw_get_ctl_max_value(struct pvr2_hdw *hdw,unsigned int ctl_id) int pvr2_hdw_set_ctl_value_internal(struct pvr2_hdw *hdw, unsigned int ctl_id,int value) { + int ret; if (ctl_id >= PVR2_CID_COUNT) return -EINVAL; if (value < control_defs[ctl_id].min_value) return -EINVAL; if (value > control_defs[ctl_id].max_value) return -EINVAL; if (control_defs[ctl_id].set_func) { - return control_defs[ctl_id].set_func(hdw,ctl_id,value); + ret = control_defs[ctl_id].set_func(hdw,ctl_id,value); + pvr2_i2c_core_check_stale(hdw); + pvr2_i2c_core_sync(hdw); + return ret; } else if (control_defs[ctl_id].get_func) { /* If there's no "set" function yet there is still a "get" function, then treat this as a read-only value. */ @@ -2062,6 +2069,28 @@ int pvr2_send_request_ex(struct pvr2_hdw *hdw, return -EINVAL; } +#if 0 + printk(KERN_INFO "pvrusb2: REQUEST BEGIN writeCnt=%u readCnt=%u", + write_len,read_len); + if (probe_fl) { + printk(" <probe>"); + } + for (idx = 0; idx < write_len; idx++) { + if (idx > 5) { + printk(" ..."); + break; + } + if (idx) { + printk(" "); + } else { + printk(" ["); + } + printk("%02x",((unsigned char *)write_data)[idx]); + } + if (write_len) printk("]"); + printk("\n"); +#endif + hdw->cmd_debug_state = 1; if (write_len) { hdw->cmd_debug_code = ((unsigned char *)write_data)[0]; @@ -2231,6 +2260,26 @@ int pvr2_send_request_ex(struct pvr2_hdw *hdw, } done: +#if 0 + printk(KERN_INFO "pvrusb2: REQUEST END status=%d",status); + if (status >= 0) { + for (idx = 0; idx < read_len; idx++) { + if (idx > 5) { + printk(" ..."); + break; + } + if (idx) { + printk(" "); + } else { + printk(" ["); + } + printk("%02x",((unsigned char *)read_data)[idx]); + } + if (read_len) printk("]"); + } + printk("\n"); +#endif + hdw->cmd_debug_state = 0; if ((status < 0) && (!probe_fl)) { pvr2_hdw_render_useless_unlocked(hdw); @@ -2406,11 +2455,11 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw) } -int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *hdw) +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw) { int status; LOCK_TAKE(hdw->ctl_lock); do { - pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc soft reset"); + pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup"); hdw->cmd_buffer[0] = 0xde; status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0); } while (0); LOCK_GIVE(hdw->ctl_lock); @@ -2418,6 +2467,27 @@ int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *hdw) } +int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw) +{ + if (!hdw->decoder_ctrl) { + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: nothing attached"); + return -ENOTTY; + } + + if (!hdw->decoder_ctrl->force_reset) { + pvr2_trace(PVR2_TRACE_INIT, + "Unable to reset decoder: not implemented"); + return -ENOTTY; + } + + pvr2_trace(PVR2_TRACE_INIT, + "Requesting decoder reset"); + hdw->decoder_ctrl->force_reset(hdw->decoder_ctrl->ctxt); + return 0; +} + + int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl) { int status; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 085afe566..aa6d9029a 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -359,7 +359,10 @@ void pvr2_hdw_device_reset(struct pvr2_hdw *); int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *); /* Execute simple reset command */ -int pvr2_hdw_cmd_soft_reset(struct pvr2_hdw *); +int pvr2_hdw_cmd_powerup(struct pvr2_hdw *); + +/* Order decoder to reset */ +int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *); /* Stop / start video stream transport */ int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c index 2d97653d5..fc9d76792 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c @@ -28,6 +28,8 @@ #include "pvrusb2-tuner.h" #include "pvrusb2-demod.h" #include "pvrusb2-video-v4l.h" +#include "pvrusb2-cx2584x-v4l.h" +#include "pvrusb2-wm8775.h" #define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__) @@ -69,18 +71,26 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) return; } } + if (id == I2C_DRIVERID_CX25840) { + if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) { + return; + } + } + if (id == I2C_DRIVERID_WM8775) { + if (pvr2_i2c_wm8775_setup(hdw,cp)) { + return; + } + } if (id == I2C_DRIVERID_SAA711X) { if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) { return; } } -#ifdef PVR2_ENABLE_DRIVERID_TDA9887 if (id == I2C_DRIVERID_TDA9887) { if (pvr2_i2c_demod_setup(hdw,cp)) { return; } } -#endif } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c index 7d137a25f..000c26911 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c @@ -238,6 +238,13 @@ const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = { }; +void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl) +{ + pvr2_i2c_client_cmd(cp, + (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),0); +} + + /* Stuff for Emacs to see, in order to encourage consistent editing style: *** Local Variables: *** diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h index fa0a3af7e..1666a3287 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h @@ -33,6 +33,8 @@ extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size; extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log; +void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int); + #endif /* __PVRUSB2_CMD_V4L2_H */ /* diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index 8ec637e5e..2f7946195 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -56,7 +56,7 @@ static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ "Killing an I2C write to %u that is too large" " (desired=%u limit=%u)", i2c_addr, - length,(sizeof(hdw->cmd_buffer) - 3)); + length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3)); return -ENOTSUPP; } @@ -88,7 +88,7 @@ static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */ } } #if 0 - trace_i2c("i2c_write(%d) len=%d ret=%d stat=%d",i2c_addr,length,ret, + trace_i2c("i2c_write(0x%x) len=%d ret=%d stat=%d",i2c_addr,length,ret, hdw->cmd_buffer[0]); #endif @@ -146,7 +146,7 @@ static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */ } #if 0 - trace_i2c("i2c_read(%d) wlen=%d rlen=%d ret=%d stat=%d", + trace_i2c("i2c_read(0x%x) wlen=%d rlen=%d ret=%d stat=%d", i2c_addr,dlen,rlen,ret,hdw->cmd_buffer[0]); #endif /* Copy back the result */ @@ -296,7 +296,7 @@ static int pvr2_i2c_control(struct i2c_adapter *adapter, static u32 pvr2_i2c_functionality(struct i2c_adapter *adap) { - return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C; + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA; } static int pvr2_i2c_core_singleton(struct i2c_client *cp, @@ -322,15 +322,26 @@ static int pvr2_i2c_core_singleton(struct i2c_client *cp, int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg) { + int stat; if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { char buf[100]; unsigned int cnt; cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, buf,sizeof(buf)); pvr2_trace(PVR2_TRACE_I2C_CMD, - "i2c COMMAND to %.*s",cnt,buf); + "i2c COMMAND (code=%u 0x%x) to %.*s", + cmd,cmd,cnt,buf); } - return pvr2_i2c_core_singleton(cp->client,cmd,arg); + stat = pvr2_i2c_core_singleton(cp->client,cmd,arg); + if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) { + char buf[100]; + unsigned int cnt; + cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG, + buf,sizeof(buf)); + pvr2_trace(PVR2_TRACE_I2C_CMD, + "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat); + } + return stat; } int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg) diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c index c554671af..c070fc195 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-main.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-main.c @@ -130,11 +130,13 @@ static int __init pvr_init(void) /* Auto-load various support modules (with which we may indirectly interact) */ - request_module("tuner"); - request_module("tveeprom"); request_module("msp3400"); + request_module("cx25840"); request_module("saa7115"); + request_module("tuner"); + request_module("tveeprom"); request_module("tda9887"); + request_module("wm8775"); class_ptr = pvr2_sysfs_class_create(); diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c index a9710b52c..f82fd643c 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-sysfs.c @@ -364,50 +364,50 @@ CREATE_STORE_INSTANCE(store_val_int,ctl_id) \ CREATE_STORE_INSTANCE(store_val_enum,ctl_id) CREATE_BATCH(0) - CREATE_BATCH(1) - CREATE_BATCH(2) - CREATE_BATCH(3) - CREATE_BATCH(4) - CREATE_BATCH(5) - CREATE_BATCH(6) - CREATE_BATCH(7) - CREATE_BATCH(8) - CREATE_BATCH(9) - CREATE_BATCH(10) - CREATE_BATCH(11) - CREATE_BATCH(12) - CREATE_BATCH(13) - CREATE_BATCH(14) - CREATE_BATCH(15) - CREATE_BATCH(16) - CREATE_BATCH(17) - CREATE_BATCH(18) - CREATE_BATCH(19) - CREATE_BATCH(20) - CREATE_BATCH(21) - CREATE_BATCH(22) - CREATE_BATCH(23) - CREATE_BATCH(24) - CREATE_BATCH(25) - CREATE_BATCH(26) - CREATE_BATCH(27) - CREATE_BATCH(28) - CREATE_BATCH(29) - CREATE_BATCH(30) - CREATE_BATCH(31) - - struct pvr2_sysfs_func_set { - ssize_t (*show_name)(struct class_device *,char *); - ssize_t (*show_min)(struct class_device *,char *); - ssize_t (*show_max)(struct class_device *,char *); - ssize_t (*show_enum)(struct class_device *,char *); - ssize_t (*show_val_int)(struct class_device *,char *); - ssize_t (*show_val_enum)(struct class_device *,char *); - ssize_t (*store_val_int)(struct class_device *, - const char *,size_t); - ssize_t (*store_val_enum)(struct class_device *, - const char *,size_t); - }; +CREATE_BATCH(1) +CREATE_BATCH(2) +CREATE_BATCH(3) +CREATE_BATCH(4) +CREATE_BATCH(5) +CREATE_BATCH(6) +CREATE_BATCH(7) +CREATE_BATCH(8) +CREATE_BATCH(9) +CREATE_BATCH(10) +CREATE_BATCH(11) +CREATE_BATCH(12) +CREATE_BATCH(13) +CREATE_BATCH(14) +CREATE_BATCH(15) +CREATE_BATCH(16) +CREATE_BATCH(17) +CREATE_BATCH(18) +CREATE_BATCH(19) +CREATE_BATCH(20) +CREATE_BATCH(21) +CREATE_BATCH(22) +CREATE_BATCH(23) +CREATE_BATCH(24) +CREATE_BATCH(25) +CREATE_BATCH(26) +CREATE_BATCH(27) +CREATE_BATCH(28) +CREATE_BATCH(29) +CREATE_BATCH(30) +CREATE_BATCH(31) + +struct pvr2_sysfs_func_set { + ssize_t (*show_name)(struct class_device *,char *); + ssize_t (*show_min)(struct class_device *,char *); + ssize_t (*show_max)(struct class_device *,char *); + ssize_t (*show_enum)(struct class_device *,char *); + ssize_t (*show_val_int)(struct class_device *,char *); + ssize_t (*show_val_enum)(struct class_device *,char *); + ssize_t (*store_val_int)(struct class_device *, + const char *,size_t); + ssize_t (*store_val_enum)(struct class_device *, + const char *,size_t); +}; #define INIT_BATCH(ctl_id) \ [ctl_id] = { \ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c index 97c3e2c38..c55f3c653 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c @@ -29,6 +29,7 @@ */ #include "pvrusb2-video-v4l.h" +#include "pvrusb2-i2c-cmd-v4l2.h" #include "pvrusb2-hdw-internal.h" @@ -176,8 +177,7 @@ static int decoder_detect(struct pvr2_i2c_client *cp) static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl) { pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl); - pvr2_i2c_client_cmd(ctxt->client, - (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),0); + pvr2_v4l2_cmd_stream(ctxt->client,fl); } diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c new file mode 100644 index 000000000..79334980c --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.c @@ -0,0 +1,171 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + + This source file is specifically designed to interface with the + wm8775. + +*/ + +#include "pvrusb2-wm8775.h" +#include "pvrusb2-i2c-cmd-v4l2.h" + + +#include "pvrusb2-hdw-internal.h" +#include "pvrusb2-debug.h" +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <linux/errno.h> +#include <linux/slab.h> + +struct pvr2_v4l_wm8775 { + struct pvr2_i2c_handler handler; + struct pvr2_i2c_client *client; + struct pvr2_hdw *hdw; + unsigned long stale_mask; +}; + + +static void set_input(struct pvr2_v4l_wm8775 *ctxt) +{ + struct v4l2_audio inp; + struct pvr2_hdw *hdw = ctxt->hdw; + int msk = 0; + + memset(&inp,0,sizeof(inp)); + + pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)", + hdw->controls[PVR2_CID_INPUT].value,msk); + + // Always point to input #1 no matter what + inp.index = 2; + pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_AUDIO,&inp); +} + + +static int check_input(struct pvr2_v4l_wm8775 *ctxt) +{ + struct pvr2_hdw *hdw = ctxt->hdw; + return hdw->controls[PVR2_CID_INPUT].dirty != 0; +} + + +struct pvr2_v4l_wm8775_ops { + void (*update)(struct pvr2_v4l_wm8775 *); + int (*check)(struct pvr2_v4l_wm8775 *); +}; + + +static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = { + { .update = set_input, .check = check_input}, +}; + + +static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt, + char *buf,unsigned int cnt) +{ + return scnprintf(buf,cnt,"handler: pvrusb2-wm8775"); +} + + +static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt) +{ + ctxt->client->handler = 0; + kfree(ctxt); +} + + +static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); + idx++) { + msk = 1 << idx; + if (ctxt->stale_mask & msk) continue; + if (wm8775_ops[idx].check(ctxt)) { + ctxt->stale_mask |= msk; + } + } + return ctxt->stale_mask != 0; +} + + +static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt) +{ + unsigned long msk; + unsigned int idx; + + for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]); + idx++) { + msk = 1 << idx; + if (!(ctxt->stale_mask & msk)) continue; + ctxt->stale_mask &= ~msk; + wm8775_ops[idx].update(ctxt); + } +} + + +const static struct pvr2_i2c_handler_functions hfuncs = { + .detach = (void (*)(void *))wm8775_detach, + .check = (int (*)(void *))wm8775_check, + .update = (void (*)(void *))wm8775_update, + .describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe, +}; + + +int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp) +{ + struct pvr2_v4l_wm8775 *ctxt; + + if (cp->handler) return 0; + + ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL); + if (!ctxt) return 0; + memset(ctxt,0,sizeof(*ctxt)); + + ctxt->handler.func_data = ctxt; + ctxt->handler.func_table = &hfuncs; + ctxt->client = cp; + ctxt->hdw = hdw; + ctxt->stale_mask = (1 << (sizeof(wm8775_ops)/ + sizeof(wm8775_ops[0]))) - 1; + cp->handler = &ctxt->handler; + pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up", + cp->client->addr); + return !0; +} + + + + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h new file mode 100644 index 000000000..15ee1e215 --- /dev/null +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-wm8775.h @@ -0,0 +1,54 @@ +/* + * + * $Id$ + * + * Copyright (C) 2005 Mike Isely <isely@pobox.com> + * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef __PVRUSB2_WM8775_H +#define __PVRUSB2_WM8775_H + +/* + + This module connects the pvrusb2 driver to the I2C chip level + driver which performs analog -> digital audio conversion for + external audio inputs. This interface is used internally by the + driver; higher level code should only interact through the + interface provided by pvrusb2-hdw.h. + +*/ + +#include "compat.h" + + +#include "pvrusb2-i2c-core.h" + +int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *); + + +#endif /* __PVRUSB2_WM8775_H */ + +/* + Stuff for Emacs to see, in order to encourage consistent editing style: + *** Local Variables: *** + *** mode: c *** + *** fill-column: 70 *** + *** tab-width: 8 *** + *** c-basic-offset: 8 *** + *** End: *** + */ |