diff --git a/Documentation/DocBook/dvb/dvbapi.xml b/Documentation/DocBook/dvb/dvbapi.xml index 4fc5b23470a33..63c528fee624a 100644 --- a/Documentation/DocBook/dvb/dvbapi.xml +++ b/Documentation/DocBook/dvb/dvbapi.xml @@ -29,6 +29,14 @@ + + 2.0.2 + 2009-10-25 + mcc + + documents FE_SET_FRONTEND_TUNE_MODE and FE_DISHETWORK_SEND_LEGACY_CMD ioctls. + + 2.0.1 2009-09-16 @@ -85,3 +93,8 @@ Added ISDB-T test originally written by Patrick Boettcher &sub-examples; + + DVB Frontend Header File + &sub-frontend-h; + + diff --git a/Documentation/DocBook/dvb/isdbt.xml b/Documentation/DocBook/dvb/dvbproperty.xml similarity index 99% rename from Documentation/DocBook/dvb/isdbt.xml rename to Documentation/DocBook/dvb/dvbproperty.xml index 92855222fccb3..5f57c7ccd4ba7 100644 --- a/Documentation/DocBook/dvb/isdbt.xml +++ b/Documentation/DocBook/dvb/dvbproperty.xml @@ -1,3 +1,6 @@ +
+FE_GET_PROPERTY/FE_SET_PROPERTY +
ISDB-T frontend This section describes shortly what are the possible parameters in the Linux @@ -312,3 +315,4 @@
+ diff --git a/Documentation/DocBook/dvb/frontend.h.xml b/Documentation/DocBook/dvb/frontend.h.xml new file mode 100644 index 0000000000000..b99644f5340a3 --- /dev/null +++ b/Documentation/DocBook/dvb/frontend.h.xml @@ -0,0 +1,415 @@ + +/* + * frontend.h + * + * Copyright (C) 2000 Marcus Metzler <marcus@convergence.de> + * Ralph Metzler <ralph@convergence.de> + * Holger Waechtler <holger@convergence.de> + * Andre Draszik <ad@convergence.de> + * for convergence integrated media GmbH + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * 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 Lesser 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 _DVBFRONTEND_H_ +#define _DVBFRONTEND_H_ + +#include <linux/types.h> + +typedef enum fe_type { + FE_QPSK, + FE_QAM, + FE_OFDM, + FE_ATSC +} fe_type_t; + + +typedef enum fe_caps { + FE_IS_STUPID = 0, + FE_CAN_INVERSION_AUTO = 0x1, + FE_CAN_FEC_1_2 = 0x2, + FE_CAN_FEC_2_3 = 0x4, + FE_CAN_FEC_3_4 = 0x8, + FE_CAN_FEC_4_5 = 0x10, + FE_CAN_FEC_5_6 = 0x20, + FE_CAN_FEC_6_7 = 0x40, + FE_CAN_FEC_7_8 = 0x80, + FE_CAN_FEC_8_9 = 0x100, + FE_CAN_FEC_AUTO = 0x200, + FE_CAN_QPSK = 0x400, + FE_CAN_QAM_16 = 0x800, + FE_CAN_QAM_32 = 0x1000, + FE_CAN_QAM_64 = 0x2000, + FE_CAN_QAM_128 = 0x4000, + FE_CAN_QAM_256 = 0x8000, + FE_CAN_QAM_AUTO = 0x10000, + FE_CAN_TRANSMISSION_MODE_AUTO = 0x20000, + FE_CAN_BANDWIDTH_AUTO = 0x40000, + FE_CAN_GUARD_INTERVAL_AUTO = 0x80000, + FE_CAN_HIERARCHY_AUTO = 0x100000, + FE_CAN_8VSB = 0x200000, + FE_CAN_16VSB = 0x400000, + FE_HAS_EXTENDED_CAPS = 0x800000, /* We need more bitspace for newer APIs, indicate this. */ + FE_CAN_2G_MODULATION = 0x10000000, /* frontend supports "2nd generation modulation" (DVB-S2) */ + FE_NEEDS_BENDING = 0x20000000, /* not supported anymore, don't use (frontend requires frequency bending) */ + FE_CAN_RECOVER = 0x40000000, /* frontend can recover from a cable unplug automatically */ + FE_CAN_MUTE_TS = 0x80000000 /* frontend can stop spurious TS data output */ +} fe_caps_t; + + +struct dvb_frontend_info { + char name[128]; + fe_type_t type; + __u32 frequency_min; + __u32 frequency_max; + __u32 frequency_stepsize; + __u32 frequency_tolerance; + __u32 symbol_rate_min; + __u32 symbol_rate_max; + __u32 symbol_rate_tolerance; /* ppm */ + __u32 notifier_delay; /* DEPRECATED */ + fe_caps_t caps; +}; + + +/** + * Check out the DiSEqC bus spec available on http://www.eutelsat.org/ for + * the meaning of this struct... + */ +struct dvb_diseqc_master_cmd { + __u8 msg [6]; /* { framing, address, command, data [3] } */ + __u8 msg_len; /* valid values are 3...6 */ +}; + + +struct dvb_diseqc_slave_reply { + __u8 msg [4]; /* { framing, data [3] } */ + __u8 msg_len; /* valid values are 0...4, 0 means no msg */ + int timeout; /* return from ioctl after timeout ms with */ +}; /* errorcode when no message was received */ + + +typedef enum fe_sec_voltage { + SEC_VOLTAGE_13, + SEC_VOLTAGE_18, + SEC_VOLTAGE_OFF +} fe_sec_voltage_t; + + +typedef enum fe_sec_tone_mode { + SEC_TONE_ON, + SEC_TONE_OFF +} fe_sec_tone_mode_t; + + +typedef enum fe_sec_mini_cmd { + SEC_MINI_A, + SEC_MINI_B +} fe_sec_mini_cmd_t; + + +typedef enum fe_status { + FE_HAS_SIGNAL = 0x01, /* found something above the noise level */ + FE_HAS_CARRIER = 0x02, /* found a DVB signal */ + FE_HAS_VITERBI = 0x04, /* FEC is stable */ + FE_HAS_SYNC = 0x08, /* found sync bytes */ + FE_HAS_LOCK = 0x10, /* everything's working... */ + FE_TIMEDOUT = 0x20, /* no lock within the last ~2 seconds */ + FE_REINIT = 0x40 /* frontend was reinitialized, */ +} fe_status_t; /* application is recommended to reset */ + /* DiSEqC, tone and parameters */ + +typedef enum fe_spectral_inversion { + INVERSION_OFF, + INVERSION_ON, + INVERSION_AUTO +} fe_spectral_inversion_t; + + +typedef enum fe_code_rate { + FEC_NONE = 0, + FEC_1_2, + FEC_2_3, + FEC_3_4, + FEC_4_5, + FEC_5_6, + FEC_6_7, + FEC_7_8, + FEC_8_9, + FEC_AUTO, + FEC_3_5, + FEC_9_10, +} fe_code_rate_t; + + +typedef enum fe_modulation { + QPSK, + QAM_16, + QAM_32, + QAM_64, + QAM_128, + QAM_256, + QAM_AUTO, + VSB_8, + VSB_16, + PSK_8, + APSK_16, + APSK_32, + DQPSK, +} fe_modulation_t; + +typedef enum fe_transmit_mode { + TRANSMISSION_MODE_2K, + TRANSMISSION_MODE_8K, + TRANSMISSION_MODE_AUTO, + TRANSMISSION_MODE_4K +} fe_transmit_mode_t; + +typedef enum fe_bandwidth { + BANDWIDTH_8_MHZ, + BANDWIDTH_7_MHZ, + BANDWIDTH_6_MHZ, + BANDWIDTH_AUTO +} fe_bandwidth_t; + + +typedef enum fe_guard_interval { + GUARD_INTERVAL_1_32, + GUARD_INTERVAL_1_16, + GUARD_INTERVAL_1_8, + GUARD_INTERVAL_1_4, + GUARD_INTERVAL_AUTO +} fe_guard_interval_t; + + +typedef enum fe_hierarchy { + HIERARCHY_NONE, + HIERARCHY_1, + HIERARCHY_2, + HIERARCHY_4, + HIERARCHY_AUTO +} fe_hierarchy_t; + + +struct dvb_qpsk_parameters { + __u32 symbol_rate; /* symbol rate in Symbols per second */ + fe_code_rate_t fec_inner; /* forward error correction (see above) */ +}; + +struct dvb_qam_parameters { + __u32 symbol_rate; /* symbol rate in Symbols per second */ + fe_code_rate_t fec_inner; /* forward error correction (see above) */ + fe_modulation_t modulation; /* modulation type (see above) */ +}; + +struct dvb_vsb_parameters { + fe_modulation_t modulation; /* modulation type (see above) */ +}; + +struct dvb_ofdm_parameters { + fe_bandwidth_t bandwidth; + fe_code_rate_t code_rate_HP; /* high priority stream code rate */ + fe_code_rate_t code_rate_LP; /* low priority stream code rate */ + fe_modulation_t constellation; /* modulation type (see above) */ + fe_transmit_mode_t transmission_mode; + fe_guard_interval_t guard_interval; + fe_hierarchy_t hierarchy_information; +}; + + +struct dvb_frontend_parameters { + __u32 frequency; /* (absolute) frequency in Hz for QAM/OFDM/ATSC */ + /* intermediate frequency in kHz for QPSK */ + fe_spectral_inversion_t inversion; + union { + struct dvb_qpsk_parameters qpsk; + struct dvb_qam_parameters qam; + struct dvb_ofdm_parameters ofdm; + struct dvb_vsb_parameters vsb; + } u; +}; + + +struct dvb_frontend_event { + fe_status_t status; + struct dvb_frontend_parameters parameters; +}; + +/* S2API Commands */ +#define DTV_UNDEFINED 0 +#define DTV_TUNE 1 +#define DTV_CLEAR 2 +#define DTV_FREQUENCY 3 +#define DTV_MODULATION 4 +#define DTV_BANDWIDTH_HZ 5 +#define DTV_INVERSION 6 +#define DTV_DISEQC_MASTER 7 +#define DTV_SYMBOL_RATE 8 +#define DTV_INNER_FEC 9 +#define DTV_VOLTAGE 10 +#define DTV_TONE 11 +#define DTV_PILOT 12 +#define DTV_ROLLOFF 13 +#define DTV_DISEQC_SLAVE_REPLY 14 + +/* Basic enumeration set for querying unlimited capabilities */ +#define DTV_FE_CAPABILITY_COUNT 15 +#define DTV_FE_CAPABILITY 16 +#define DTV_DELIVERY_SYSTEM 17 + +/* ISDB-T and ISDB-Tsb */ +#define DTV_ISDBT_PARTIAL_RECEPTION 18 +#define DTV_ISDBT_SOUND_BROADCASTING 19 + +#define DTV_ISDBT_SB_SUBCHANNEL_ID 20 +#define DTV_ISDBT_SB_SEGMENT_IDX 21 +#define DTV_ISDBT_SB_SEGMENT_COUNT 22 + +#define DTV_ISDBT_LAYERA_FEC 23 +#define DTV_ISDBT_LAYERA_MODULATION 24 +#define DTV_ISDBT_LAYERA_SEGMENT_COUNT 25 +#define DTV_ISDBT_LAYERA_TIME_INTERLEAVING 26 + +#define DTV_ISDBT_LAYERB_FEC 27 +#define DTV_ISDBT_LAYERB_MODULATION 28 +#define DTV_ISDBT_LAYERB_SEGMENT_COUNT 29 +#define DTV_ISDBT_LAYERB_TIME_INTERLEAVING 30 + +#define DTV_ISDBT_LAYERC_FEC 31 +#define DTV_ISDBT_LAYERC_MODULATION 32 +#define DTV_ISDBT_LAYERC_SEGMENT_COUNT 33 +#define DTV_ISDBT_LAYERC_TIME_INTERLEAVING 34 + +#define DTV_API_VERSION 35 + +#define DTV_CODE_RATE_HP 36 +#define DTV_CODE_RATE_LP 37 +#define DTV_GUARD_INTERVAL 38 +#define DTV_TRANSMISSION_MODE 39 +#define DTV_HIERARCHY 40 + +#define DTV_ISDBT_LAYER_ENABLED 41 + +#define DTV_ISDBS_TS_ID 42 + +#define DTV_MAX_COMMAND DTV_ISDBS_TS_ID + +typedef enum fe_pilot { + PILOT_ON, + PILOT_OFF, + PILOT_AUTO, +} fe_pilot_t; + +typedef enum fe_rolloff { + ROLLOFF_35, /* Implied value in DVB-S, default for DVB-S2 */ + ROLLOFF_20, + ROLLOFF_25, + ROLLOFF_AUTO, +} fe_rolloff_t; + +typedef enum fe_delivery_system { + SYS_UNDEFINED, + SYS_DVBC_ANNEX_AC, + SYS_DVBC_ANNEX_B, + SYS_DVBT, + SYS_DSS, + SYS_DVBS, + SYS_DVBS2, + SYS_DVBH, + SYS_ISDBT, + SYS_ISDBS, + SYS_ISDBC, + SYS_ATSC, + SYS_ATSCMH, + SYS_DMBTH, + SYS_CMMB, + SYS_DAB, +} fe_delivery_system_t; + +struct dtv_cmds_h { + char *name; /* A display name for debugging purposes */ + + __u32 cmd; /* A unique ID */ + + /* Flags */ + __u32 set:1; /* Either a set or get property */ + __u32 buffer:1; /* Does this property use the buffer? */ + __u32 reserved:30; /* Align */ +}; + +struct dtv_property { + __u32 cmd; + __u32 reserved[3]; + union { + __u32 data; + struct { + __u8 data[32]; + __u32 len; + __u32 reserved1[3]; + void *reserved2; + } buffer; + } u; + int result; +} __attribute__ ((packed)); + +/* num of properties cannot exceed DTV_IOCTL_MAX_MSGS per ioctl */ +#define DTV_IOCTL_MAX_MSGS 64 + +struct dtv_properties { + __u32 num; + struct dtv_property *props; +}; + +#define FE_SET_PROPERTY _IOW('o', 82, struct dtv_properties) +#define FE_GET_PROPERTY _IOR('o', 83, struct dtv_properties) + + +/** + * When set, this flag will disable any zigzagging or other "normal" tuning + * behaviour. Additionally, there will be no automatic monitoring of the lock + * status, and hence no frontend events will be generated. If a frontend device + * is closed, this flag will be automatically turned off when the device is + * reopened read-write. + */ +#define FE_TUNE_MODE_ONESHOT 0x01 + + +#define FE_GET_INFO _IOR('o', 61, struct dvb_frontend_info) + +#define FE_DISEQC_RESET_OVERLOAD _IO('o', 62) +#define FE_DISEQC_SEND_MASTER_CMD _IOW('o', 63, struct dvb_diseqc_master_cmd) +#define FE_DISEQC_RECV_SLAVE_REPLY _IOR('o', 64, struct dvb_diseqc_slave_reply) +#define FE_DISEQC_SEND_BURST _IO('o', 65) /* fe_sec_mini_cmd_t */ + +#define FE_SET_TONE _IO('o', 66) /* fe_sec_tone_mode_t */ +#define FE_SET_VOLTAGE _IO('o', 67) /* fe_sec_voltage_t */ +#define FE_ENABLE_HIGH_LNB_VOLTAGE _IO('o', 68) /* int */ + +#define FE_READ_STATUS _IOR('o', 69, fe_status_t) +#define FE_READ_BER _IOR('o', 70, __u32) +#define FE_READ_SIGNAL_STRENGTH _IOR('o', 71, __u16) +#define FE_READ_SNR _IOR('o', 72, __u16) +#define FE_READ_UNCORRECTED_BLOCKS _IOR('o', 73, __u32) + +#define FE_SET_FRONTEND _IOW('o', 76, struct dvb_frontend_parameters) +#define FE_GET_FRONTEND _IOR('o', 77, struct dvb_frontend_parameters) +#define FE_SET_FRONTEND_TUNE_MODE _IO('o', 81) /* unsigned int */ +#define FE_GET_EVENT _IOR('o', 78, struct dvb_frontend_event) + +#define FE_DISHNETWORK_SEND_LEGACY_CMD _IO('o', 80) /* unsigned int */ + +#endif /*_DVBFRONTEND_H_*/ + diff --git a/Documentation/DocBook/dvb/frontend.xml b/Documentation/DocBook/dvb/frontend.xml index 9d89a7b94fd5e..300ba1f041778 100644 --- a/Documentation/DocBook/dvb/frontend.xml +++ b/Documentation/DocBook/dvb/frontend.xml @@ -73,7 +73,8 @@ a specific frontend type.
frontend information -Information about the frontend ca be queried with FE_GET_INFO. +Information about the frontend ca be queried with + FE_GET_INFO. struct dvb_frontend_info { @@ -338,7 +339,7 @@ modulation mode which can be one of the following: This system call opens a named frontend device (/dev/dvb/adapter0/frontend0) for subsequent use. Usually the first thing to do after a successful open is to - find out the frontend type with FE_GET_INFO. + find out the frontend type with FE_GET_INFO. The device can be opened in read-only mode, which only allows monitoring of device status and statistics, or read/write mode, which allows any kind of use (e.g. performing tuning operations.) @@ -478,7 +479,7 @@ modulation mode which can be one of the following:
-
+
FE_READ_STATUS DESCRIPTION @@ -492,7 +493,7 @@ modulation mode which can be one of the following: -int ioctl(int fd, int request = FE_READ_STATUS, +int ioctl(int fd, int request = FE_READ_STATUS, fe_status_t ⋆status); @@ -511,7 +512,7 @@ modulation mode which can be one of the following: int request -Equals FE_READ_STATUS for this command. +Equals FE_READ_STATUS for this command. @@ -542,7 +543,7 @@ modulation mode which can be one of the following:
-
+
FE_READ_BER DESCRIPTION @@ -557,7 +558,7 @@ modulation mode which can be one of the following: -int ioctl(int fd, int request = FE_READ_BER, +int ioctl(int fd, int request = FE_READ_BER, uint32_t ⋆ber); @@ -575,7 +576,7 @@ modulation mode which can be one of the following: int request -Equals FE_READ_BER for this command. +Equals FE_READ_BER for this command. @@ -619,7 +620,7 @@ modulation mode which can be one of the following:
-
+
FE_READ_SNR DESCRIPTION @@ -634,7 +635,7 @@ modulation mode which can be one of the following: -int ioctl(int fd, int request = FE_READ_SNR, int16_t +int ioctl(int fd, int request = FE_READ_SNR, int16_t ⋆snr); @@ -652,7 +653,7 @@ modulation mode which can be one of the following: int request -Equals FE_READ_SNR for this command. +Equals FE_READ_SNR for this command. @@ -697,7 +698,7 @@ modulation mode which can be one of the following:
-
+
FE_READ_SIGNAL_STRENGTH DESCRIPTION @@ -712,7 +713,7 @@ modulation mode which can be one of the following: int ioctl( int fd, int request = - FE_READ_SIGNAL_STRENGTH, int16_t ⋆strength); + FE_READ_SIGNAL_STRENGTH, int16_t ⋆strength); @@ -730,7 +731,7 @@ modulation mode which can be one of the following: int request -Equals FE_READ_SIGNAL_STRENGTH for this +Equals FE_READ_SIGNAL_STRENGTH for this command.
-
+
FE_READ_UNCORRECTED_BLOCKS DESCRIPTION @@ -797,7 +798,7 @@ modulation mode which can be one of the following: int ioctl( int fd, int request = - FE_READ_UNCORRECTED_BLOCKS, uint32_t ⋆ublocks); + FE_READ_UNCORRECTED_BLOCKS, uint32_t ⋆ublocks); PARAMETERS @@ -814,7 +815,7 @@ modulation mode which can be one of the following: int request -Equals FE_READ_UNCORRECTED_BLOCKS for this +Equals FE_READ_UNCORRECTED_BLOCKS for this command.
-
+
FE_SET_FRONTEND DESCRIPTION @@ -861,8 +862,8 @@ modulation mode which can be one of the following: This ioctl call starts a tuning operation using specified parameters. The result of this call will be successful if the parameters were valid and the tuning could be initiated. The result of the tuning operation in itself, however, will arrive - asynchronously as an event (see documentation for FE_GET_EVENT and - FrontendEvent.) If a new FE_SET_FRONTEND operation is initiated before + asynchronously as an event (see documentation for FE_GET_EVENT and + FrontendEvent.) If a new FE_SET_FRONTEND operation is initiated before the previous one was completed, the previous operation will be aborted in favor of the new one. This command requires read/write access to the device. @@ -872,7 +873,7 @@ modulation mode which can be one of the following: -int ioctl(int fd, int request = FE_SET_FRONTEND, +int ioctl(int fd, int request = FE_SET_FRONTEND, struct dvb_frontend_parameters ⋆p); @@ -890,7 +891,7 @@ modulation mode which can be one of the following: int request -Equals FE_SET_FRONTEND for this command. +Equals FE_SET_FRONTEND for this command. @@ -928,7 +929,7 @@ modulation mode which can be one of the following:
-
+
FE_GET_FRONTEND DESCRIPTION @@ -943,7 +944,7 @@ modulation mode which can be one of the following: -int ioctl(int fd, int request = FE_GET_FRONTEND, +int ioctl(int fd, int request = FE_GET_FRONTEND, struct dvb_frontend_parameters ⋆p); @@ -962,7 +963,7 @@ modulation mode which can be one of the following: int request -Equals FE_SET_FRONTEND for this command. +Equals FE_SET_FRONTEND for this command. @@ -1003,7 +1004,7 @@ modulation mode which can be one of the following:
-
+
FE_GET_EVENT DESCRIPTION @@ -1024,7 +1025,8 @@ modulation mode which can be one of the following: rather small (room for 8 events), the queue must be serviced regularly to avoid overflow. If an overflow happens, the oldest event is discarded from the queue, and an error (EOVERFLOW) occurs the next time the queue is read. After - reporting the error condition in this fashion, subsequent FE_GET_EVENT + reporting the error condition in this fashion, subsequent + FE_GET_EVENT calls will return events from the queue as usual. int request -Equals FE_GET_EVENT for this command. +Equals FE_GET_EVENT for this command. @@ -1115,7 +1117,7 @@ modulation mode which can be one of the following:
-
+
FE_GET_INFO DESCRIPTION @@ -1130,7 +1132,7 @@ modulation mode which can be one of the following: - int ioctl(int fd, int request = FE_GET_INFO, struct + int ioctl(int fd, int request = FE_GET_INFO, struct dvb_frontend_info ⋆info); @@ -1149,7 +1151,7 @@ modulation mode which can be one of the following: int request -Equals FE_GET_INFO for this command. +Equals FE_GET_INFO for this command. @@ -1181,7 +1183,7 @@ modulation mode which can be one of the following:
-
+
FE_DISEQC_RESET_OVERLOAD DESCRIPTION @@ -1199,7 +1201,7 @@ modulation mode which can be one of the following: int ioctl(int fd, int request = - FE_DISEQC_RESET_OVERLOAD); + FE_DISEQC_RESET_OVERLOAD); PARAMETERS @@ -1216,7 +1218,7 @@ modulation mode which can be one of the following: int request -Equals FE_DISEQC_RESET_OVERLOAD for this +Equals FE_DISEQC_RESET_OVERLOAD for this command. @@ -1247,7 +1249,7 @@ modulation mode which can be one of the following:
-
+
FE_DISEQC_SEND_MASTER_CMD DESCRIPTION @@ -1261,7 +1263,7 @@ modulation mode which can be one of the following: int ioctl(int fd, int request = - FE_DISEQC_SEND_MASTER_CMD, struct + FE_DISEQC_SEND_MASTER_CMD, struct dvb_diseqc_master_cmd ⋆cmd); @@ -1280,7 +1282,7 @@ modulation mode which can be one of the following: int request -Equals FE_DISEQC_SEND_MASTER_CMD for this +Equals FE_DISEQC_SEND_MASTER_CMD for this command.
-
+
FE_DISEQC_RECV_SLAVE_REPLY DESCRIPTION @@ -1350,7 +1352,7 @@ modulation mode which can be one of the following: int ioctl(int fd, int request = - FE_DISEQC_RECV_SLAVE_REPLY, struct + FE_DISEQC_RECV_SLAVE_REPLY, struct dvb_diseqc_slave_reply ⋆reply); @@ -1369,7 +1371,7 @@ modulation mode which can be one of the following: int request -Equals FE_DISEQC_RECV_SLAVE_REPLY for this +Equals FE_DISEQC_RECV_SLAVE_REPLY for this command.
-
+
FE_DISEQC_SEND_BURST DESCRIPTION @@ -1438,7 +1440,7 @@ modulation mode which can be one of the following: int ioctl(int fd, int request = - FE_DISEQC_SEND_BURST, fe_sec_mini_cmd_t burst); + FE_DISEQC_SEND_BURST, fe_sec_mini_cmd_t burst); @@ -1456,7 +1458,7 @@ modulation mode which can be one of the following: int request -Equals FE_DISEQC_SEND_BURST for this command. +Equals FE_DISEQC_SEND_BURST for this command. @@ -1509,7 +1511,7 @@ modulation mode which can be one of the following:
-
+
FE_SET_TONE DESCRIPTION @@ -1523,7 +1525,7 @@ modulation mode which can be one of the following: -int ioctl(int fd, int request = FE_SET_TONE, +int ioctl(int fd, int request = FE_SET_TONE, fe_sec_tone_mode_t tone); @@ -1541,7 +1543,7 @@ modulation mode which can be one of the following: int request -Equals FE_SET_TONE for this command. +Equals FE_SET_TONE for this command. @@ -1592,7 +1594,7 @@ modulation mode which can be one of the following:
-
+
FE_SET_VOLTAGE DESCRIPTION @@ -1606,7 +1608,7 @@ modulation mode which can be one of the following: -int ioctl(int fd, int request = FE_SET_VOLTAGE, +int ioctl(int fd, int request = FE_SET_VOLTAGE, fe_sec_voltage_t voltage); @@ -1625,7 +1627,7 @@ modulation mode which can be one of the following: int request -Equals FE_SET_VOLTAGE for this command. +Equals FE_SET_VOLTAGE for this command. @@ -1677,7 +1679,7 @@ modulation mode which can be one of the following:
-
+
FE_ENABLE_HIGH_LNB_VOLTAGE DESCRIPTION @@ -1694,7 +1696,7 @@ modulation mode which can be one of the following: int ioctl(int fd, int request = - FE_ENABLE_HIGH_LNB_VOLTAGE, int high); + FE_ENABLE_HIGH_LNB_VOLTAGE, int high); @@ -1712,7 +1714,7 @@ modulation mode which can be one of the following: int request -Equals FE_SET_VOLTAGE for this command. +Equals FE_SET_VOLTAGE for this command. @@ -1762,5 +1764,82 @@ modulation mode which can be one of the following:
+ +
+FE_SET_FRONTEND_TUNE_MODE +DESCRIPTION + + +Allow setting tuner mode flags to the frontend. + + + +SYNOPSIS + + +int ioctl(int fd, int request = +FE_SET_FRONTEND_TUNE_MODE, unsigned int flags); + + + +PARAMETERS + + + unsigned int flags + + + +FE_TUNE_MODE_ONESHOT When set, this flag will disable any zigzagging or other "normal" tuning behaviour. Additionally, there will be no automatic monitoring of the lock status, and hence no frontend events will be generated. If a frontend device is closed, this flag will be automatically turned off when the device is reopened read-write. + + + + +ERRORS + +EINVAL +Invalid argument. +
-&sub-isdbt; + +
+ FE_DISHNETWORK_SEND_LEGACY_CMD +DESCRIPTION + + +WARNING: This is a very obscure legacy command, used only at stv0299 driver. Should not be used on newer drivers. +It provides a non-standard method for selecting Diseqc voltage on the frontend, for Dish Network legacy switches. +As support for this ioctl were added in 2004, this means that such dishes were already legacy in 2004. + + + +SYNOPSIS + + +int ioctl(int fd, int request = + FE_DISHNETWORK_SEND_LEGACY_CMD, unsigned long cmd); + + + +PARAMETERS + + + unsigned long cmd + + + +sends the specified raw cmd to the dish via DISEqC. + + + + +ERRORS + + + There are no errors in use for this call + + +
+ +
+ +&sub-dvbproperty; diff --git a/Documentation/DocBook/media-entities.tmpl b/Documentation/DocBook/media-entities.tmpl index 0eb43c1970bbd..bb5ab741220ef 100644 --- a/Documentation/DocBook/media-entities.tmpl +++ b/Documentation/DocBook/media-entities.tmpl @@ -280,7 +280,7 @@ - + @@ -288,6 +288,7 @@ + diff --git a/Documentation/DocBook/v4l/controls.xml b/Documentation/DocBook/v4l/controls.xml index f492accb691de..f464506104127 100644 --- a/Documentation/DocBook/v4l/controls.xml +++ b/Documentation/DocBook/v4l/controls.xml @@ -280,11 +280,29 @@ minimum value disables backlight compensation. V4L2_COLORFX_BW (1) and V4L2_COLORFX_SEPIA (2). + + V4L2_CID_ROTATE + integer + Rotates the image by specified angle. Common angles are 90, + 270 and 180. Rotating the image to 90 and 270 will reverse the height + and width of the display window. It is necessary to set the new height and + width of the picture using the &VIDIOC-S-FMT; ioctl according to + the rotation angle selected. + + + V4L2_CID_BG_COLOR + integer + Sets the background color on the current output device. + Background color needs to be specified in the RGB24 format. The + supplied 32 bit value is interpreted as bits 0-7 Red color information, + bits 8-15 Green color information, bits 16-23 Blue color + information and bits 24-31 must be zero. + V4L2_CID_LASTP1 End of the predefined control IDs (currently -V4L2_CID_COLORFX + 1). +V4L2_CID_BG_COLOR + 1). V4L2_CID_PRIVATE_BASE diff --git a/Documentation/DocBook/v4l/pixfmt.xml b/Documentation/DocBook/v4l/pixfmt.xml index 7d396a3785f5d..885968d6a2fc7 100644 --- a/Documentation/DocBook/v4l/pixfmt.xml +++ b/Documentation/DocBook/v4l/pixfmt.xml @@ -770,6 +770,11 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'S920' YUV 4:2:0 format of the gspca sn9c20x driver. + + V4L2_PIX_FMT_STV0680 + 'S680' + Bayer format of the gspca stv0680 driver. + V4L2_PIX_FMT_WNVA 'WNVA' diff --git a/Documentation/DocBook/v4l/videodev2.h.xml b/Documentation/DocBook/v4l/videodev2.h.xml index 26303e58345f0..3e282ed9f5938 100644 --- a/Documentation/DocBook/v4l/videodev2.h.xml +++ b/Documentation/DocBook/v4l/videodev2.h.xml @@ -363,6 +363,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */ #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ #define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ +#define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */ /* * F O R M A T E N U M E R A T I O N @@ -565,6 +566,7 @@ struct v4l2_framebuffer { #define V4L2_FBUF_CAP_LOCAL_ALPHA 0x0010 #define V4L2_FBUF_CAP_GLOBAL_ALPHA 0x0020 #define V4L2_FBUF_CAP_LOCAL_INV_ALPHA 0x0040 +#define V4L2_FBUF_CAP_SRC_CHROMAKEY 0x0080 /* Flags for the 'flags' field. */ #define V4L2_FBUF_FLAG_PRIMARY 0x0001 #define V4L2_FBUF_FLAG_OVERLAY 0x0002 @@ -572,6 +574,7 @@ struct v4l2_framebuffer { #define V4L2_FBUF_FLAG_LOCAL_ALPHA 0x0008 #define V4L2_FBUF_FLAG_GLOBAL_ALPHA 0x0010 #define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA 0x0020 +#define V4L2_FBUF_FLAG_SRC_CHROMAKEY 0x0040 struct v4l2_clip { struct v4l2_rect c; @@ -914,8 +917,10 @@ enum v4l2_colorfx { #define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32) #define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33) +#define V4L2_CID_ROTATE (V4L2_CID_BASE+34) +#define V4L2_CID_BG_COLOR (V4L2_CID_BASE+35) /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+34) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+36) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) diff --git a/Documentation/DocBook/v4l/vidioc-g-fbuf.xml b/Documentation/DocBook/v4l/vidioc-g-fbuf.xml index f7017062656e7..e7dda4822f048 100644 --- a/Documentation/DocBook/v4l/vidioc-g-fbuf.xml +++ b/Documentation/DocBook/v4l/vidioc-g-fbuf.xml @@ -336,6 +336,13 @@ alpha value. Alpha blending makes no sense for destructive overlays. inverted alpha channel of the framebuffer or VGA signal. Alpha blending makes no sense for destructive overlays. + + V4L2_FBUF_CAP_SRC_CHROMAKEY + 0x0080 + The device supports Source Chroma-keying. Framebuffer pixels +with the chroma-key colors are replaced by video pixels, which is exactly opposite of +V4L2_FBUF_CAP_CHROMAKEY + @@ -411,6 +418,16 @@ images, but with an inverted alpha value. The blend function is: output = framebuffer pixel * (1 - alpha) + video pixel * alpha. The actual alpha depth depends on the framebuffer pixel format. + + V4L2_FBUF_FLAG_SRC_CHROMAKEY + 0x0040 + Use source chroma-keying. The source chroma-key color is +determined by the chromakey field of +&v4l2-window; and negotiated with the &VIDIOC-S-FMT; ioctl, see and . +Both chroma-keying are mutual exclusive to each other, so same +chromakey field of &v4l2-window; is being used. + diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885 index 5f33d8486102b..7539e8fa1ffd9 100644 --- a/Documentation/video4linux/CARDLIST.cx23885 +++ b/Documentation/video4linux/CARDLIST.cx23885 @@ -24,3 +24,5 @@ 23 -> Magic-Pro ProHDTV Extreme 2 [14f1:8657] 24 -> Hauppauge WinTV-HVR1850 [0070:8541] 25 -> Compro VideoMate E800 [1858:e800] + 26 -> Hauppauge WinTV-HVR1290 [0070:8551] + 27 -> Mygica X8558 PRO DMB-TH [14f1:8578] diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88 index 3385f8b094a5e..7ec3c4e4b60f4 100644 --- a/Documentation/video4linux/CARDLIST.cx88 +++ b/Documentation/video4linux/CARDLIST.cx88 @@ -81,3 +81,4 @@ 80 -> Hauppauge WinTV-IR Only [0070:9290] 81 -> Leadtek WinFast DTV1800 Hybrid [107d:6654] 82 -> WinFast DTV2000 H rev. J [107d:6f2b] + 83 -> Prof 7301 DVB-S/S2 [b034:3034] diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx index b8afef4c0e01b..0c166ff003a00 100644 --- a/Documentation/video4linux/CARDLIST.em28xx +++ b/Documentation/video4linux/CARDLIST.em28xx @@ -1,5 +1,5 @@ 0 -> Unknown EM2800 video grabber (em2800) [eb1a:2800] - 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2710,eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2870,eb1a:2881,eb1a:2883,eb1a:2868] + 1 -> Unknown EM2750/28xx video grabber (em2820/em2840) [eb1a:2710,eb1a:2820,eb1a:2821,eb1a:2860,eb1a:2861,eb1a:2862,eb1a:2870,eb1a:2881,eb1a:2883,eb1a:2868] 2 -> Terratec Cinergy 250 USB (em2820/em2840) [0ccd:0036] 3 -> Pinnacle PCTV USB 2 (em2820/em2840) [2304:0208] 4 -> Hauppauge WinTV USB 2 (em2820/em2840) [2040:4200,2040:4201] @@ -69,3 +69,4 @@ 71 -> Silvercrest Webcam 1.3mpix (em2820/em2840) 72 -> Gadmei UTV330+ (em2861) 73 -> Reddo DVB-C USB TV Box (em2870) + 74 -> Actionmaster/LinXcel/Digitus VC211A (em2800) diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134 index 2620d60341eed..fce1e7eb04748 100644 --- a/Documentation/video4linux/CARDLIST.saa7134 +++ b/Documentation/video4linux/CARDLIST.saa7134 @@ -172,3 +172,5 @@ 171 -> Beholder BeholdTV X7 [5ace:7595] 172 -> RoverMedia TV Link Pro FM [19d1:0138] 173 -> Zolid Hybrid TV Tuner PCI [1131:2004] +174 -> Asus Europa Hybrid OEM [1043:4847] +175 -> Leadtek Winfast DTV1000S [107d:6655] diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index 6b29555b58b70..319d9838e87e0 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -7,6 +7,7 @@ The modules are: xxxx vend:prod ---- spca501 0000:0000 MystFromOri Unknown Camera +spca508 0130:0130 Clone Digital Webcam 11043 m5602 0402:5602 ALi Video Camera Controller spca501 040a:0002 Kodak DVC-325 spca500 040a:0300 Kodak EZ200 @@ -37,6 +38,7 @@ ov519 041e:405f Creative Live! VISTA VF0330 ov519 041e:4060 Creative Live! VISTA VF0350 ov519 041e:4061 Creative Live! VISTA VF0400 ov519 041e:4064 Creative Live! VISTA VF0420 +ov519 041e:4067 Creative Live! Cam Video IM (VF0350) ov519 041e:4068 Creative Live! VISTA VF0470 spca561 0458:7004 Genius VideoCAM Express V2 sunplus 0458:7006 Genius Dsc 1.3 Smart @@ -68,12 +70,12 @@ zc3xx 046d:08a3 Logitech QC Chat zc3xx 046d:08a6 Logitech QCim zc3xx 046d:08a7 Logitech QuickCam Image zc3xx 046d:08a9 Logitech Notebook Deluxe -zc3xx 046d:08aa Labtec Webcam Notebook +zc3xx 046d:08aa Labtec Webcam Notebook zc3xx 046d:08ac Logitech QuickCam Cool zc3xx 046d:08ad Logitech QCCommunicate STX zc3xx 046d:08ae Logitech QuickCam for Notebooks zc3xx 046d:08af Logitech QuickCam Cool -zc3xx 046d:08b9 Logitech QC IM ??? +zc3xx 046d:08b9 Logitech QuickCam Express zc3xx 046d:08d7 Logitech QCam STX zc3xx 046d:08d9 Logitech QuickCam IM/Connect zc3xx 046d:08d8 Logitech Notebook Deluxe @@ -82,7 +84,7 @@ zc3xx 046d:08dd Logitech QuickCam for Notebooks spca500 046d:0900 Logitech Inc. ClickSmart 310 spca500 046d:0901 Logitech Inc. ClickSmart 510 sunplus 046d:0905 Logitech ClickSmart 820 -tv8532 046d:0920 QC Express +tv8532 046d:0920 Logitech QuickCam Express tv8532 046d:0921 Labtec Webcam spca561 046d:0928 Logitech QC Express Etch2 spca561 046d:0929 Labtec Webcam Elch2 @@ -91,7 +93,7 @@ spca561 046d:092b Labtec Webcam Plus spca561 046d:092c Logitech QC chat Elch2 spca561 046d:092d Logitech QC Elch2 spca561 046d:092e Logitech QC Elch2 -spca561 046d:092f Logitech QuickCam Express Plus +spca561 046d:092f Logitech QuickCam Express Plus sunplus 046d:0960 Logitech ClickSmart 420 sunplus 0471:0322 Philips DMVC1300K zc3xx 0471:0325 Philips SPC 200 NC @@ -187,7 +189,6 @@ sonixj 06f8:3004 Hercules Classic Silver sonixj 06f8:3008 Hercules Deluxe Optical Glass pac7311 06f8:3009 Hercules Classic Link spca508 0733:0110 ViewQuest VQ110 -spca508 0130:0130 Clone Digital Webcam 11043 spca501 0733:0401 Intel Create and Share spca501 0733:0402 ViewQuest M318B spca505 0733:0430 Intel PC Camera Pro @@ -202,6 +203,7 @@ spca500 084d:0003 D-Link DSC-350 spca500 08ca:0103 Aiptek PocketDV sunplus 08ca:0104 Aiptek PocketDVII 1.3 sunplus 08ca:0106 Aiptek Pocket DV3100+ +mr97310a 08ca:0110 Trust Spyc@m 100 mr97310a 08ca:0111 Aiptek PenCam VGA+ sunplus 08ca:2008 Aiptek Mini PenCam 2 M sunplus 08ca:2010 Aiptek PocketCam 3M @@ -222,7 +224,7 @@ pac207 093a:2460 Qtec Webcam 100 pac207 093a:2461 HP Webcam pac207 093a:2463 Philips SPC 220 NC pac207 093a:2464 Labtec Webcam 1200 -pac207 093a:2468 PAC207 +pac207 093a:2468 Webcam WB-1400T pac207 093a:2470 Genius GF112 pac207 093a:2471 Genius VideoCam ge111 pac207 093a:2472 Genius VideoCam ge110 @@ -230,7 +232,7 @@ pac207 093a:2474 Genius iLook 111 pac207 093a:2476 Genius e-Messenger 112 pac7311 093a:2600 PAC7311 Typhoon pac7311 093a:2601 Philips SPC 610 NC -pac7311 093a:2603 PAC7312 +pac7311 093a:2603 Philips SPC 500 NC pac7311 093a:2608 Trust WB-3300p pac7311 093a:260e Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350 pac7311 093a:260f SnakeCam @@ -239,6 +241,7 @@ pac7311 093a:2621 PAC731x pac7311 093a:2622 Genius Eye 312 pac7311 093a:2624 PAC7302 pac7311 093a:2626 Labtec 2200 +pac7311 093a:2628 Genius iLook 300 pac7311 093a:2629 Genious iSlim 300 pac7311 093a:262a Webcam 300k pac7311 093a:262c Philips SPC 230 NC @@ -250,7 +253,7 @@ vc032x 0ac8:0328 A4Tech PK-130MG zc3xx 0ac8:301b Z-Star zc301b zc3xx 0ac8:303b Vimicro 0x303b zc3xx 0ac8:305b Z-star Vimicro zc0305b -zc3xx 0ac8:307b Ldlc VC302+Ov7620 +zc3xx 0ac8:307b PC Camera (ZS0211) vc032x 0ac8:c001 Sony embedded vimicro vc032x 0ac8:c002 Sony embedded vimicro vc032x 0ac8:c301 Samsung Q1 Ultra Premium diff --git a/Documentation/video4linux/si4713.txt b/Documentation/video4linux/si4713.txt index 25abdb78209dd..2e7392a4fee1a 100644 --- a/Documentation/video4linux/si4713.txt +++ b/Documentation/video4linux/si4713.txt @@ -164,7 +164,7 @@ Stereo/Mono and RDS subchannels The device can also be configured using the available sub channels for transmission. To do that use S/G_MODULATOR ioctl and configure txsubchans properly. -Refer to v4l2-spec for proper use of this ioctl. +Refer to the V4L2 API specification for proper use of this ioctl. Testing ======= diff --git a/Documentation/video4linux/zr364xx.txt b/Documentation/video4linux/zr364xx.txt index 7f3d1955d214f..d98e4d302977d 100644 --- a/Documentation/video4linux/zr364xx.txt +++ b/Documentation/video4linux/zr364xx.txt @@ -66,3 +66,4 @@ Vendor Product Distributor Model 0x0a17 0x004e Pentax Optio 50 0x041e 0x405d Creative DiVi CAM 516 0x08ca 0x2102 Aiptek DV T300 +0x06d6 0x003d Trust Powerc@m 910Z diff --git a/drivers/media/common/Makefile b/drivers/media/common/Makefile index 351b98b9b302a..169b337b7c9df 100644 --- a/drivers/media/common/Makefile +++ b/drivers/media/common/Makefile @@ -1,6 +1,6 @@ saa7146-objs := saa7146_i2c.o saa7146_core.o saa7146_vv-objs := saa7146_fops.o saa7146_video.o saa7146_hlp.o saa7146_vbi.o -ir-common-objs := ir-functions.o ir-keymaps.o +ir-common-objs := ir-functions.o ir-keymaps.o ir-keytable.o obj-y += tuners/ obj-$(CONFIG_VIDEO_SAA7146) += saa7146.o diff --git a/drivers/media/common/ir-functions.c b/drivers/media/common/ir-functions.c index abd4791acb0ec..e616f624ceaac 100644 --- a/drivers/media/common/ir-functions.c +++ b/drivers/media/common/ir-functions.c @@ -34,22 +34,19 @@ static int repeat = 1; module_param(repeat, int, 0444); MODULE_PARM_DESC(repeat,"auto-repeat for IR keys (default: on)"); -static int debug; /* debug level (0,1,2) */ -module_param(debug, int, 0644); - -#define dprintk(level, fmt, arg...) if (debug >= level) \ - printk(KERN_DEBUG fmt , ## arg) +int media_ir_debug; /* media_ir_debug level (0,1,2) */ +module_param_named(debug, media_ir_debug, int, 0644); /* -------------------------------------------------------------------------- */ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) { if (KEY_RESERVED == ir->keycode) { - printk(KERN_INFO "%s: unknown key: key=0x%02x raw=0x%02x down=%d\n", - dev->name,ir->ir_key,ir->ir_raw,ir->keypressed); + printk(KERN_INFO "%s: unknown key: key=0x%02x down=%d\n", + dev->name, ir->ir_key, ir->keypressed); return; } - dprintk(1,"%s: key event code=%d down=%d\n", + IR_dprintk(1,"%s: key event code=%d down=%d\n", dev->name,ir->keycode,ir->keypressed); input_report_key(dev,ir->keycode,ir->keypressed); input_sync(dev); @@ -57,39 +54,34 @@ static void ir_input_key_event(struct input_dev *dev, struct ir_input_state *ir) /* -------------------------------------------------------------------------- */ -void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, +int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, int ir_type, struct ir_scancode_table *ir_codes) { - int i; - ir->ir_type = ir_type; - memset(ir->ir_codes, 0, sizeof(ir->ir_codes)); + ir->keytable.size = ir_roundup_tablesize(ir_codes->size); + ir->keytable.scan = kzalloc(ir->keytable.size * + sizeof(struct ir_scancode), GFP_KERNEL); + if (!ir->keytable.scan) + return -ENOMEM; - /* - * FIXME: This is a temporary workaround to use the new IR tables - * with the old approach. Later patches will replace this to a - * proper method - */ + IR_dprintk(1, "Allocated space for %d keycode entries (%zd bytes)\n", + ir->keytable.size, + ir->keytable.size * sizeof(ir->keytable.scan)); - if (ir_codes) - for (i = 0; i < ir_codes->size; i++) - if (ir_codes->scan[i].scancode < IR_KEYTAB_SIZE) - ir->ir_codes[ir_codes->scan[i].scancode] = ir_codes->scan[i].keycode; + ir_copy_table(&ir->keytable, ir_codes); + ir_set_keycode_table(dev, &ir->keytable); - dev->keycode = ir->ir_codes; - dev->keycodesize = sizeof(IR_KEYTAB_TYPE); - dev->keycodemax = IR_KEYTAB_SIZE; - for (i = 0; i < IR_KEYTAB_SIZE; i++) - set_bit(ir->ir_codes[i], dev->keybit); clear_bit(0, dev->keybit); - set_bit(EV_KEY, dev->evbit); if (repeat) set_bit(EV_REP, dev->evbit); + + return 0; } EXPORT_SYMBOL_GPL(ir_input_init); + void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) { if (ir->keypressed) { @@ -100,9 +92,9 @@ void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir) EXPORT_SYMBOL_GPL(ir_input_nokey); void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, - u32 ir_key, u32 ir_raw) + u32 ir_key) { - u32 keycode = IR_KEYCODE(ir->ir_codes, ir_key); + u32 keycode = ir_g_keycode_from_table(dev, ir_key); if (ir->keypressed && ir->keycode != keycode) { ir->keypressed = 0; @@ -110,7 +102,6 @@ void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, } if (!ir->keypressed) { ir->ir_key = ir_key; - ir->ir_raw = ir_raw; ir->keycode = keycode; ir->keypressed = 1; ir_input_key_event(dev,ir); @@ -275,7 +266,7 @@ EXPORT_SYMBOL_GPL(ir_decode_biphase); * saa7134 */ /* decode raw bit pattern to RC5 code */ -static u32 ir_rc5_decode(unsigned int code) +u32 ir_rc5_decode(unsigned int code) { unsigned int org_code = code; unsigned int pair; @@ -295,15 +286,16 @@ static u32 ir_rc5_decode(unsigned int code) rc5 |= 1; break; case 3: - dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code); + IR_dprintk(1, "ir-common: ir_rc5_decode(%x) bad code\n", org_code); return 0; } } - dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " + IR_dprintk(1, "ir-common: code=%x, rc5=%x, start=%x, toggle=%x, address=%x, " "instr=%x\n", rc5, org_code, RC5_START(rc5), RC5_TOGGLE(rc5), RC5_ADDR(rc5), RC5_INSTR(rc5)); return rc5; } +EXPORT_SYMBOL_GPL(ir_rc5_decode); void ir_rc5_timer_end(unsigned long data) { @@ -330,20 +322,20 @@ void ir_rc5_timer_end(unsigned long data) /* Allow some timer jitter (RC5 is ~24ms anyway so this is ok) */ if (gap < 28000) { - dprintk(1, "ir-common: spurious timer_end\n"); + IR_dprintk(1, "ir-common: spurious timer_end\n"); return; } if (ir->last_bit < 20) { /* ignore spurious codes (caused by light/other remotes) */ - dprintk(1, "ir-common: short code: %x\n", ir->code); + IR_dprintk(1, "ir-common: short code: %x\n", ir->code); } else { ir->code = (ir->code << ir->shift_by) | 1; rc5 = ir_rc5_decode(ir->code); /* two start bits? */ if (RC5_START(rc5) != ir->start) { - dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5)); + IR_dprintk(1, "ir-common: rc5 start bits invalid: %u\n", RC5_START(rc5)); /* right address? */ } else if (RC5_ADDR(rc5) == ir->addr) { @@ -353,11 +345,10 @@ void ir_rc5_timer_end(unsigned long data) /* Good code, decide if repeat/repress */ if (toggle != RC5_TOGGLE(ir->last_rc5) || instr != RC5_INSTR(ir->last_rc5)) { - dprintk(1, "ir-common: instruction %x, toggle %x\n", instr, + IR_dprintk(1, "ir-common: instruction %x, toggle %x\n", instr, toggle); ir_input_nokey(ir->dev, &ir->ir); - ir_input_keydown(ir->dev, &ir->ir, instr, - instr); + ir_input_keydown(ir->dev, &ir->ir, instr); } /* Set/reset key-up timer */ @@ -376,7 +367,7 @@ void ir_rc5_timer_keyup(unsigned long data) { struct card_ir *ir = (struct card_ir *)data; - dprintk(1, "ir-common: key released\n"); + IR_dprintk(1, "ir-common: key released\n"); ir_input_nokey(ir->dev, &ir->ir); } EXPORT_SYMBOL_GPL(ir_rc5_timer_keyup); diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c index f6790172736a0..328c973a08383 100644 --- a/drivers/media/common/ir-keymaps.c +++ b/drivers/media/common/ir-keymaps.c @@ -1705,6 +1705,7 @@ static struct ir_scancode ir_codes_winfast[] = { { 0x37, KEY_RADIO }, /* FM */ { 0x38, KEY_DVD }, + { 0x1a, KEY_MODE}, /* change to MCE mode on Y04G0051 */ { 0x3e, KEY_F21 }, /* MCE +VOL, on Y04G0033 */ { 0x3a, KEY_F22 }, /* MCE -VOL, on Y04G0033 */ { 0x3b, KEY_F23 }, /* MCE +CH, on Y04G0033 */ @@ -1846,6 +1847,76 @@ struct ir_scancode_table ir_codes_hauppauge_new_table = { }; EXPORT_SYMBOL_GPL(ir_codes_hauppauge_new_table); +/* + * Hauppauge:the newer, gray remotes (seems there are multiple + * slightly different versions), shipped with cx88+ivtv cards. + * + * This table contains the complete RC5 code, instead of just the data part + */ +static struct ir_scancode ir_codes_rc5_hauppauge_new[] = { + /* Keys 0 to 9 */ + { 0x1e00, KEY_0 }, + { 0x1e01, KEY_1 }, + { 0x1e02, KEY_2 }, + { 0x1e03, KEY_3 }, + { 0x1e04, KEY_4 }, + { 0x1e05, KEY_5 }, + { 0x1e06, KEY_6 }, + { 0x1e07, KEY_7 }, + { 0x1e08, KEY_8 }, + { 0x1e09, KEY_9 }, + + { 0x1e0a, KEY_TEXT }, /* keypad asterisk as well */ + { 0x1e0b, KEY_RED }, /* red button */ + { 0x1e0c, KEY_RADIO }, + { 0x1e0d, KEY_MENU }, + { 0x1e0e, KEY_SUBTITLE }, /* also the # key */ + { 0x1e0f, KEY_MUTE }, + { 0x1e10, KEY_VOLUMEUP }, + { 0x1e11, KEY_VOLUMEDOWN }, + { 0x1e12, KEY_PREVIOUS }, /* previous channel */ + { 0x1e14, KEY_UP }, + { 0x1e15, KEY_DOWN }, + { 0x1e16, KEY_LEFT }, + { 0x1e17, KEY_RIGHT }, + { 0x1e18, KEY_VIDEO }, /* Videos */ + { 0x1e19, KEY_AUDIO }, /* Music */ + /* 0x1e1a: Pictures - presume this means + "Multimedia Home Platform" - + no "PICTURES" key in input.h + */ + { 0x1e1a, KEY_MHP }, + + { 0x1e1b, KEY_EPG }, /* Guide */ + { 0x1e1c, KEY_TV }, + { 0x1e1e, KEY_NEXTSONG }, /* skip >| */ + { 0x1e1f, KEY_EXIT }, /* back/exit */ + { 0x1e20, KEY_CHANNELUP }, /* channel / program + */ + { 0x1e21, KEY_CHANNELDOWN }, /* channel / program - */ + { 0x1e22, KEY_CHANNEL }, /* source (old black remote) */ + { 0x1e24, KEY_PREVIOUSSONG }, /* replay |< */ + { 0x1e25, KEY_ENTER }, /* OK */ + { 0x1e26, KEY_SLEEP }, /* minimize (old black remote) */ + { 0x1e29, KEY_BLUE }, /* blue key */ + { 0x1e2e, KEY_GREEN }, /* green button */ + { 0x1e30, KEY_PAUSE }, /* pause */ + { 0x1e32, KEY_REWIND }, /* backward << */ + { 0x1e34, KEY_FASTFORWARD }, /* forward >> */ + { 0x1e35, KEY_PLAY }, + { 0x1e36, KEY_STOP }, + { 0x1e37, KEY_RECORD }, /* recording */ + { 0x1e38, KEY_YELLOW }, /* yellow key */ + { 0x1e3b, KEY_SELECT }, /* top right button */ + { 0x1e3c, KEY_ZOOM }, /* full */ + { 0x1e3d, KEY_POWER }, /* system power (green button) */ +}; + +struct ir_scancode_table ir_codes_rc5_hauppauge_new_table = { + .scan = ir_codes_rc5_hauppauge_new, + .size = ARRAY_SIZE(ir_codes_rc5_hauppauge_new), +}; +EXPORT_SYMBOL_GPL(ir_codes_rc5_hauppauge_new_table); + static struct ir_scancode ir_codes_npgtech[] = { { 0x1d, KEY_SWITCHVIDEOMODE }, /* switch inputs */ { 0x2a, KEY_FRONT }, @@ -2964,6 +3035,101 @@ struct ir_scancode_table ir_codes_dm1105_nec_table = { }; EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec_table); +static struct ir_scancode ir_codes_tevii_nec[] = { + { 0x0a, KEY_POWER2}, + { 0x0c, KEY_MUTE}, + { 0x11, KEY_1}, + { 0x12, KEY_2}, + { 0x13, KEY_3}, + { 0x14, KEY_4}, + { 0x15, KEY_5}, + { 0x16, KEY_6}, + { 0x17, KEY_7}, + { 0x18, KEY_8}, + { 0x19, KEY_9}, + { 0x10, KEY_0}, + { 0x1c, KEY_MENU}, + { 0x0f, KEY_VOLUMEDOWN}, + { 0x1a, KEY_LAST}, + { 0x0e, KEY_OPEN}, + { 0x04, KEY_RECORD}, + { 0x09, KEY_VOLUMEUP}, + { 0x08, KEY_CHANNELUP}, + { 0x07, KEY_PVR}, + { 0x0b, KEY_TIME}, + { 0x02, KEY_RIGHT}, + { 0x03, KEY_LEFT}, + { 0x00, KEY_UP}, + { 0x1f, KEY_OK}, + { 0x01, KEY_DOWN}, + { 0x05, KEY_TUNER}, + { 0x06, KEY_CHANNELDOWN}, + { 0x40, KEY_PLAYPAUSE}, + { 0x1e, KEY_REWIND}, + { 0x1b, KEY_FAVORITES}, + { 0x1d, KEY_BACK}, + { 0x4d, KEY_FASTFORWARD}, + { 0x44, KEY_EPG}, + { 0x4c, KEY_INFO}, + { 0x41, KEY_AB}, + { 0x43, KEY_AUDIO}, + { 0x45, KEY_SUBTITLE}, + { 0x4a, KEY_LIST}, + { 0x46, KEY_F1}, + { 0x47, KEY_F2}, + { 0x5e, KEY_F3}, + { 0x5c, KEY_F4}, + { 0x52, KEY_F5}, + { 0x5a, KEY_F6}, + { 0x56, KEY_MODE}, + { 0x58, KEY_SWITCHVIDEOMODE}, +}; +struct ir_scancode_table ir_codes_tevii_nec_table = { + .scan = ir_codes_tevii_nec, + .size = ARRAY_SIZE(ir_codes_tevii_nec), +}; +EXPORT_SYMBOL_GPL(ir_codes_tevii_nec_table); + +static struct ir_scancode ir_codes_tbs_nec[] = { + { 0x04, KEY_POWER2}, /*power*/ + { 0x14, KEY_MUTE}, /*mute*/ + { 0x07, KEY_1}, + { 0x06, KEY_2}, + { 0x05, KEY_3}, + { 0x0b, KEY_4}, + { 0x0a, KEY_5}, + { 0x09, KEY_6}, + { 0x0f, KEY_7}, + { 0x0e, KEY_8}, + { 0x0d, KEY_9}, + { 0x12, KEY_0}, + { 0x16, KEY_CHANNELUP}, /*ch+*/ + { 0x11, KEY_CHANNELDOWN},/*ch-*/ + { 0x13, KEY_VOLUMEUP}, /*vol+*/ + { 0x0c, KEY_VOLUMEDOWN},/*vol-*/ + { 0x03, KEY_RECORD}, /*rec*/ + { 0x18, KEY_PAUSE}, /*pause*/ + { 0x19, KEY_OK}, /*ok*/ + { 0x1a, KEY_CAMERA}, /* snapshot */ + { 0x01, KEY_UP}, + { 0x10, KEY_LEFT}, + { 0x02, KEY_RIGHT}, + { 0x08, KEY_DOWN}, + { 0x15, KEY_FAVORITES}, + { 0x17, KEY_SUBTITLE}, + { 0x1d, KEY_ZOOM}, + { 0x1f, KEY_EXIT}, + { 0x1e, KEY_MENU}, + { 0x1c, KEY_EPG}, + { 0x00, KEY_PREVIOUS}, + { 0x1b, KEY_MODE}, +}; +struct ir_scancode_table ir_codes_tbs_nec_table = { + .scan = ir_codes_tbs_nec, + .size = ARRAY_SIZE(ir_codes_tbs_nec), +}; +EXPORT_SYMBOL_GPL(ir_codes_tbs_nec_table); + /* Terratec Cinergy Hybrid T USB XS Devin Heitmueller */ @@ -3147,3 +3313,4 @@ struct ir_scancode_table ir_codes_gadmei_rm008z_table = { .size = ARRAY_SIZE(ir_codes_gadmei_rm008z), }; EXPORT_SYMBOL_GPL(ir_codes_gadmei_rm008z_table); + diff --git a/drivers/media/common/ir-keytable.c b/drivers/media/common/ir-keytable.c new file mode 100644 index 0000000000000..26ce5bc2fdd54 --- /dev/null +++ b/drivers/media/common/ir-keytable.c @@ -0,0 +1,429 @@ +/* ir-register.c - handle IR scancode->keycode tables + * + * Copyright (C) 2009 by Mauro Carvalho Chehab + */ + +#include + +#include + +#define IR_TAB_MIN_SIZE 32 +#define IR_TAB_MAX_SIZE 1024 + +/** + * ir_seek_table() - returns the element order on the table + * @rc_tab: the ir_scancode_table with the keymap to be used + * @scancode: the scancode that we're seeking + * + * This routine is used by the input routines when a key is pressed at the + * IR. The scancode is received and needs to be converted into a keycode. + * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the + * corresponding keycode from the table. + */ +static int ir_seek_table(struct ir_scancode_table *rc_tab, u32 scancode) +{ + int rc; + unsigned long flags; + struct ir_scancode *keymap = rc_tab->scan; + + spin_lock_irqsave(&rc_tab->lock, flags); + + /* FIXME: replace it by a binary search */ + + for (rc = 0; rc < rc_tab->size; rc++) + if (keymap[rc].scancode == scancode) + goto exit; + + /* Not found */ + rc = -EINVAL; + +exit: + spin_unlock_irqrestore(&rc_tab->lock, flags); + return rc; +} + +/** + * ir_roundup_tablesize() - gets an optimum value for the table size + * @n_elems: minimum number of entries to store keycodes + * + * This routine is used to choose the keycode table size. + * + * In order to have some empty space for new keycodes, + * and knowing in advance that kmalloc allocates only power of two + * segments, it optimizes the allocated space to have some spare space + * for those new keycodes by using the maximum number of entries that + * will be effectively be allocated by kmalloc. + * In order to reduce the quantity of table resizes, it has a minimum + * table size of IR_TAB_MIN_SIZE. + */ +int ir_roundup_tablesize(int n_elems) +{ + size_t size; + + if (n_elems < IR_TAB_MIN_SIZE) + n_elems = IR_TAB_MIN_SIZE; + + /* + * As kmalloc only allocates sizes of power of two, get as + * much entries as possible for the allocated memory segment + */ + size = roundup_pow_of_two(n_elems * sizeof(struct ir_scancode)); + n_elems = size / sizeof(struct ir_scancode); + + return n_elems; +} + +/** + * ir_copy_table() - copies a keytable, discarding the unused entries + * @destin: destin table + * @origin: origin table + * + * Copies all entries where the keycode is not KEY_UNKNOWN/KEY_RESERVED + */ + +int ir_copy_table(struct ir_scancode_table *destin, + const struct ir_scancode_table *origin) +{ + int i, j = 0; + + for (i = 0; i < origin->size; i++) { + if (origin->scan[i].keycode == KEY_UNKNOWN || + origin->scan[i].keycode == KEY_RESERVED) + continue; + + memcpy(&destin->scan[j], &origin->scan[i], sizeof(struct ir_scancode)); + j++; + } + destin->size = j; + + IR_dprintk(1, "Copied %d scancodes to the new keycode table\n", destin->size); + + return 0; +} + +/** + * ir_getkeycode() - get a keycode at the evdev scancode ->keycode table + * @dev: the struct input_dev device descriptor + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + * This routine is used to handle evdev EVIOCGKEY ioctl. + * If the key is not found, returns -EINVAL, otherwise, returns 0. + */ +static int ir_getkeycode(struct input_dev *dev, + int scancode, int *keycode) +{ + int elem; + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + + elem = ir_seek_table(rc_tab, scancode); + if (elem >= 0) { + *keycode = rc_tab->scan[elem].keycode; + return 0; + } + + /* + * Scancode not found and table can't be expanded + */ + if (elem < 0 && rc_tab->size == IR_TAB_MAX_SIZE) + return -EINVAL; + + /* + * If is there extra space, returns KEY_RESERVED, + * otherwise, input core won't let ir_setkeycode to work + */ + *keycode = KEY_RESERVED; + return 0; +} + + +/** + * ir_is_resize_needed() - Check if the table needs rezise + * @table: keycode table that may need to resize + * @n_elems: minimum number of entries to store keycodes + * + * Considering that kmalloc uses power of two storage areas, this + * routine detects if the real alloced size will change. If not, it + * just returns without doing nothing. Otherwise, it will extend or + * reduce the table size to meet the new needs. + * + * It returns 0 if no resize is needed, 1 otherwise. + */ +static int ir_is_resize_needed(struct ir_scancode_table *table, int n_elems) +{ + int cur_size = ir_roundup_tablesize(table->size); + int new_size = ir_roundup_tablesize(n_elems); + + if (cur_size == new_size) + return 0; + + /* Resize is needed */ + return 1; +} + +/** + * ir_delete_key() - remove a keycode from the table + * @rc_tab: keycode table + * @elem: element to be removed + * + */ +static void ir_delete_key(struct ir_scancode_table *rc_tab, int elem) +{ + unsigned long flags = 0; + int newsize = rc_tab->size - 1; + int resize = ir_is_resize_needed(rc_tab, newsize); + struct ir_scancode *oldkeymap = rc_tab->scan; + struct ir_scancode *newkeymap; + + if (resize) { + newkeymap = kzalloc(ir_roundup_tablesize(newsize) * + sizeof(*newkeymap), GFP_ATOMIC); + + /* There's no memory for resize. Keep the old table */ + if (!newkeymap) + resize = 0; + } + + if (!resize) { + newkeymap = oldkeymap; + + /* We'll modify the live table. Lock it */ + spin_lock_irqsave(&rc_tab->lock, flags); + } + + /* + * Copy the elements before the one that will be deleted + * if (!resize), both oldkeymap and newkeymap points + * to the same place, so, there's no need to copy + */ + if (resize && elem > 0) + memcpy(newkeymap, oldkeymap, + elem * sizeof(*newkeymap)); + + /* + * Copy the other elements overwriting the element to be removed + * This operation applies to both resize and non-resize case + */ + if (elem < newsize) + memcpy(&newkeymap[elem], &oldkeymap[elem + 1], + (newsize - elem) * sizeof(*newkeymap)); + + if (resize) { + /* + * As the copy happened to a temporary table, only here + * it needs to lock while replacing the table pointers + * to use the new table + */ + spin_lock_irqsave(&rc_tab->lock, flags); + rc_tab->size = newsize; + rc_tab->scan = newkeymap; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + /* Frees the old keytable */ + kfree(oldkeymap); + } else { + rc_tab->size = newsize; + spin_unlock_irqrestore(&rc_tab->lock, flags); + } +} + +/** + * ir_insert_key() - insert a keycode at the table + * @rc_tab: keycode table + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + */ +static int ir_insert_key(struct ir_scancode_table *rc_tab, + int scancode, int keycode) +{ + unsigned long flags; + int elem = rc_tab->size; + int newsize = rc_tab->size + 1; + int resize = ir_is_resize_needed(rc_tab, newsize); + struct ir_scancode *oldkeymap = rc_tab->scan; + struct ir_scancode *newkeymap; + + if (resize) { + newkeymap = kzalloc(ir_roundup_tablesize(newsize) * + sizeof(*newkeymap), GFP_ATOMIC); + if (!newkeymap) + return -ENOMEM; + + memcpy(newkeymap, oldkeymap, + rc_tab->size * sizeof(*newkeymap)); + } else + newkeymap = oldkeymap; + + /* Stores the new code at the table */ + IR_dprintk(1, "#%d: New scan 0x%04x with key 0x%04x\n", + rc_tab->size, scancode, keycode); + + spin_lock_irqsave(&rc_tab->lock, flags); + rc_tab->size = newsize; + if (resize) { + rc_tab->scan = newkeymap; + kfree(oldkeymap); + } + newkeymap[elem].scancode = scancode; + newkeymap[elem].keycode = keycode; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + return 0; +} + +/** + * ir_setkeycode() - set a keycode at the evdev scancode ->keycode table + * @dev: the struct input_dev device descriptor + * @scancode: the desired scancode + * @keycode: the keycode to be retorned. + * + * This routine is used to handle evdev EVIOCSKEY ioctl. + * There's one caveat here: how can we increase the size of the table? + * If the key is not found, returns -EINVAL, otherwise, returns 0. + */ +static int ir_setkeycode(struct input_dev *dev, + int scancode, int keycode) +{ + int rc = 0; + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + struct ir_scancode *keymap = rc_tab->scan; + unsigned long flags; + + /* + * Handle keycode table deletions + * + * If userspace is adding a KEY_UNKNOWN or KEY_RESERVED, + * deal as a trial to remove an existing scancode attribution + * if table become too big, reduce it to save space + */ + if (keycode == KEY_UNKNOWN || keycode == KEY_RESERVED) { + rc = ir_seek_table(rc_tab, scancode); + if (rc < 0) + return 0; + + IR_dprintk(1, "#%d: Deleting scan 0x%04x\n", rc, scancode); + clear_bit(keymap[rc].keycode, dev->keybit); + ir_delete_key(rc_tab, rc); + + return 0; + } + + /* + * Handle keycode replacements + * + * If the scancode exists, just replace by the new value + */ + rc = ir_seek_table(rc_tab, scancode); + if (rc >= 0) { + IR_dprintk(1, "#%d: Replacing scan 0x%04x with key 0x%04x\n", + rc, scancode, keycode); + + clear_bit(keymap[rc].keycode, dev->keybit); + + spin_lock_irqsave(&rc_tab->lock, flags); + keymap[rc].keycode = keycode; + spin_unlock_irqrestore(&rc_tab->lock, flags); + + set_bit(keycode, dev->keybit); + + return 0; + } + + /* + * Handle new scancode inserts + * + * reallocate table if needed and insert a new keycode + */ + + /* Avoid growing the table indefinitely */ + if (rc_tab->size + 1 > IR_TAB_MAX_SIZE) + return -EINVAL; + + rc = ir_insert_key(rc_tab, scancode, keycode); + if (rc < 0) + return rc; + set_bit(keycode, dev->keybit); + + return 0; +} + +/** + * ir_g_keycode_from_table() - gets the keycode that corresponds to a scancode + * @input_dev: the struct input_dev descriptor of the device + * @scancode: the scancode that we're seeking + * + * This routine is used by the input routines when a key is pressed at the + * IR. The scancode is received and needs to be converted into a keycode. + * If the key is not found, it returns KEY_UNKNOWN. Otherwise, returns the + * corresponding keycode from the table. + */ +u32 ir_g_keycode_from_table(struct input_dev *dev, u32 scancode) +{ + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + struct ir_scancode *keymap = rc_tab->scan; + int elem; + + elem = ir_seek_table(rc_tab, scancode); + if (elem >= 0) { + IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n", + dev->name, scancode, keymap[elem].keycode); + + return rc_tab->scan[elem].keycode; + } + + printk(KERN_INFO "%s: unknown key for scancode 0x%04x\n", + dev->name, scancode); + + /* Reports userspace that an unknown keycode were got */ + return KEY_RESERVED; +} + +/** + * ir_set_keycode_table() - sets the IR keycode table and add the handlers + * for keymap table get/set + * @input_dev: the struct input_dev descriptor of the device + * @rc_tab: the struct ir_scancode_table table of scancode/keymap + * + * This routine is used to initialize the input infrastructure to work with + * an IR. + * It should be called before registering the IR device. + */ +int ir_set_keycode_table(struct input_dev *input_dev, + struct ir_scancode_table *rc_tab) +{ + struct ir_scancode *keymap = rc_tab->scan; + int i; + + spin_lock_init(&rc_tab->lock); + + if (rc_tab->scan == NULL || !rc_tab->size) + return -EINVAL; + + /* set the bits for the keys */ + IR_dprintk(1, "key map size: %d\n", rc_tab->size); + for (i = 0; i < rc_tab->size; i++) { + IR_dprintk(1, "#%d: setting bit for keycode 0x%04x\n", + i, keymap[i].keycode); + set_bit(keymap[i].keycode, input_dev->keybit); + } + + input_dev->getkeycode = ir_getkeycode; + input_dev->setkeycode = ir_setkeycode; + input_set_drvdata(input_dev, rc_tab); + + return 0; +} + +void ir_input_free(struct input_dev *dev) +{ + struct ir_scancode_table *rc_tab = input_get_drvdata(dev); + + IR_dprintk(1, "Freed keycode table\n"); + + rc_tab->size = 0; + kfree(rc_tab->scan); + rc_tab->scan = NULL; +} +EXPORT_SYMBOL_GPL(ir_input_free); + diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index 552dab442d78d..becbaadb3b774 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -1205,6 +1205,13 @@ static int buffer_activate (struct saa7146_dev *dev, return 0; } +static void release_all_pagetables(struct saa7146_dev *dev, struct saa7146_buf *buf) +{ + saa7146_pgtable_free(dev->pci, &buf->pt[0]); + saa7146_pgtable_free(dev->pci, &buf->pt[1]); + saa7146_pgtable_free(dev->pci, &buf->pt[2]); +} + static int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, enum v4l2_field field) { @@ -1257,16 +1264,12 @@ static int buffer_prepare(struct videobuf_queue *q, sfmt = format_by_fourcc(dev,buf->fmt->pixelformat); + release_all_pagetables(dev, buf); if( 0 != IS_PLANAR(sfmt->trans)) { - saa7146_pgtable_free(dev->pci, &buf->pt[0]); - saa7146_pgtable_free(dev->pci, &buf->pt[1]); - saa7146_pgtable_free(dev->pci, &buf->pt[2]); - saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); saa7146_pgtable_alloc(dev->pci, &buf->pt[1]); saa7146_pgtable_alloc(dev->pci, &buf->pt[2]); } else { - saa7146_pgtable_free(dev->pci, &buf->pt[0]); saa7146_pgtable_alloc(dev->pci, &buf->pt[0]); } @@ -1329,6 +1332,9 @@ static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_CAP(("vbuf:%p\n",vb)); + + release_all_pagetables(dev, buf); + saa7146_dma_free(dev,q,buf); } diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 607d319ce8ed8..409a4261e5b5f 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -172,4 +172,11 @@ config MEDIA_TUNER_MC44S803 help Say Y here to support the Freescale MC44S803 based tuners +config MEDIA_TUNER_MAX2165 + tristate "Maxim MAX2165 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + A driver for the silicon tuner MAX2165 from Maxim. + endif # MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index 4132b2be79e54..a5438523f30d7 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -23,6 +23,7 @@ obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o +obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/max2165.c b/drivers/media/common/tuners/max2165.c new file mode 100644 index 0000000000000..1b486cfb8ed98 --- /dev/null +++ b/drivers/media/common/tuners/max2165.c @@ -0,0 +1,442 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" + +#include "max2165.h" +#include "max2165_priv.h" +#include "tuner-i2c.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "max2165: " args); \ + } while (0) + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off)."); + +static int max2165_write_reg(struct max2165_priv *priv, u8 reg, u8 data) +{ + int ret; + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .flags = 0, .buf = buf, .len = 2 }; + + msg.addr = priv->config->i2c_address; + + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg, 1); + + if (ret != 1) + dprintk(KERN_DEBUG "%s: error reg=0x%x, data=0x%x, ret=%i\n", + __func__, reg, data, ret); + + return (ret != 1) ? -EIO : 0; +} + +static int max2165_read_reg(struct max2165_priv *priv, u8 reg, u8 *p_data) +{ + int ret; + u8 dev_addr = priv->config->i2c_address; + + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { .addr = dev_addr, .flags = 0, .buf = b0, .len = 1 }, + { .addr = dev_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 }, + }; + + ret = i2c_transfer(priv->i2c, msg, 2); + if (ret != 2) { + dprintk(KERN_DEBUG "%s: error reg=0x%x, ret=%i\n", + __func__, reg, ret); + return -EIO; + } + + *p_data = b1[0]; + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%02X, data=0x%02X\n", + __func__, reg, b1[0]); + return 0; +} + +static int max2165_mask_write_reg(struct max2165_priv *priv, u8 reg, + u8 mask, u8 data) +{ + int ret; + u8 v; + + data &= mask; + ret = max2165_read_reg(priv, reg, &v); + if (ret != 0) + return ret; + v &= ~mask; + v |= data; + ret = max2165_write_reg(priv, reg, v); + + return ret; +} + +static int max2165_read_rom_table(struct max2165_priv *priv) +{ + u8 dat[3]; + int i; + + for (i = 0; i < 3; i++) { + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, i + 1); + max2165_read_reg(priv, REG_ROM_TABLE_DATA, &dat[i]); + } + + priv->tf_ntch_low_cfg = dat[0] >> 4; + priv->tf_ntch_hi_cfg = dat[0] & 0x0F; + priv->tf_balun_low_ref = dat[1] & 0x0F; + priv->tf_balun_hi_ref = dat[1] >> 4; + priv->bb_filter_7mhz_cfg = dat[2] & 0x0F; + priv->bb_filter_8mhz_cfg = dat[2] >> 4; + + dprintk("tf_ntch_low_cfg = 0x%X\n", priv->tf_ntch_low_cfg); + dprintk("tf_ntch_hi_cfg = 0x%X\n", priv->tf_ntch_hi_cfg); + dprintk("tf_balun_low_ref = 0x%X\n", priv->tf_balun_low_ref); + dprintk("tf_balun_hi_ref = 0x%X\n", priv->tf_balun_hi_ref); + dprintk("bb_filter_7mhz_cfg = 0x%X\n", priv->bb_filter_7mhz_cfg); + dprintk("bb_filter_8mhz_cfg = 0x%X\n", priv->bb_filter_8mhz_cfg); + + return 0; +} + +static int max2165_set_osc(struct max2165_priv *priv, u8 osc /*MHz*/) +{ + u8 v; + + v = (osc / 2); + if (v == 2) + v = 0x7; + else + v -= 8; + + max2165_mask_write_reg(priv, REG_PLL_CFG, 0x07, v); + + return 0; +} + +static int max2165_set_bandwidth(struct max2165_priv *priv, u32 bw) +{ + u8 val; + + if (bw == BANDWIDTH_8_MHZ) + val = priv->bb_filter_8mhz_cfg; + else + val = priv->bb_filter_7mhz_cfg; + + max2165_mask_write_reg(priv, REG_BASEBAND_CTRL, 0xF0, val << 4); + + return 0; +} + +int fixpt_div32(u32 dividend, u32 divisor, u32 *quotient, u32 *fraction) +{ + u32 remainder; + u32 q, f = 0; + int i; + + if (0 == divisor) + return -1; + + q = dividend / divisor; + remainder = dividend - q * divisor; + + for (i = 0; i < 31; i++) { + remainder <<= 1; + if (remainder >= divisor) { + f += 1; + remainder -= divisor; + } + f <<= 1; + } + + *quotient = q; + *fraction = f; + + return 0; +} + +static int max2165_set_rf(struct max2165_priv *priv, u32 freq) +{ + u8 tf; + u8 tf_ntch; + double t; + u32 quotient, fraction; + + /* Set PLL divider according to RF frequency */ + fixpt_div32(freq / 1000, priv->config->osc_clk * 1000, + "ient, &fraction); + + /* 20-bit fraction */ + fraction >>= 12; + + max2165_write_reg(priv, REG_NDIV_INT, quotient); + max2165_mask_write_reg(priv, REG_NDIV_FRAC2, 0x0F, fraction >> 16); + max2165_write_reg(priv, REG_NDIV_FRAC1, fraction >> 8); + max2165_write_reg(priv, REG_NDIV_FRAC0, fraction); + + /* Norch Filter */ + tf_ntch = (freq < 725000000) ? + priv->tf_ntch_low_cfg : priv->tf_ntch_hi_cfg; + + /* Tracking filter balun */ + t = priv->tf_balun_low_ref; + t += (priv->tf_balun_hi_ref - priv->tf_balun_low_ref) + * (freq / 1000 - 470000) / (780000 - 470000); + + tf = t; + dprintk("tf = %X\n", tf); + tf |= tf_ntch << 4; + + max2165_write_reg(priv, REG_TRACK_FILTER, tf); + + return 0; +} + +static void max2165_debug_status(struct max2165_priv *priv) +{ + u8 status, autotune; + u8 auto_vco_success, auto_vco_active; + u8 pll_locked; + u8 dc_offset_low, dc_offset_hi; + u8 signal_lv_over_threshold; + u8 vco, vco_sub_band, adc; + + max2165_read_reg(priv, REG_STATUS, &status); + max2165_read_reg(priv, REG_AUTOTUNE, &autotune); + + auto_vco_success = (status >> 6) & 0x01; + auto_vco_active = (status >> 5) & 0x01; + pll_locked = (status >> 4) & 0x01; + dc_offset_low = (status >> 3) & 0x01; + dc_offset_hi = (status >> 2) & 0x01; + signal_lv_over_threshold = status & 0x01; + + vco = autotune >> 6; + vco_sub_band = (autotune >> 3) & 0x7; + adc = autotune & 0x7; + + dprintk("auto VCO active: %d, auto VCO success: %d\n", + auto_vco_active, auto_vco_success); + dprintk("PLL locked: %d\n", pll_locked); + dprintk("DC offset low: %d, DC offset high: %d\n", + dc_offset_low, dc_offset_hi); + dprintk("Signal lvl over threshold: %d\n", signal_lv_over_threshold); + dprintk("VCO: %d, VCO Sub-band: %d, ADC: %d\n", vco, vco_sub_band, adc); +} + +static int max2165_set_params(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct max2165_priv *priv = fe->tuner_priv; + int ret; + + dprintk("%s() frequency=%d (Hz)\n", __func__, params->frequency); + if (fe->ops.info.type == FE_ATSC) { + return -EINVAL; + } else if (fe->ops.info.type == FE_OFDM) { + dprintk("%s() OFDM\n", __func__); + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + return -EINVAL; + case BANDWIDTH_7_MHZ: + case BANDWIDTH_8_MHZ: + priv->frequency = params->frequency; + priv->bandwidth = params->u.ofdm.bandwidth; + break; + default: + printk(KERN_ERR "MAX2165 bandwidth not set!\n"); + return -EINVAL; + } + } else { + printk(KERN_ERR "MAX2165 modulation type not supported!\n"); + return -EINVAL; + } + + dprintk("%s() frequency=%d\n", __func__, priv->frequency); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + max2165_set_bandwidth(priv, priv->bandwidth); + ret = max2165_set_rf(priv, priv->frequency); + mdelay(50); + max2165_debug_status(priv); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + if (ret != 0) + return -EREMOTEIO; + + return 0; +} + +static int max2165_get_frequency(struct dvb_frontend *fe, u32 *freq) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + *freq = priv->frequency; + return 0; +} + +static int max2165_get_bandwidth(struct dvb_frontend *fe, u32 *bw) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + *bw = priv->bandwidth; + return 0; +} + +static int max2165_get_status(struct dvb_frontend *fe, u32 *status) +{ + struct max2165_priv *priv = fe->tuner_priv; + u16 lock_status = 0; + + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + max2165_debug_status(priv); + *status = lock_status; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_sleep(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +static int max2165_init(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + + /* Setup initial values */ + /* Fractional Mode on */ + max2165_write_reg(priv, REG_NDIV_FRAC2, 0x18); + /* LNA on */ + max2165_write_reg(priv, REG_LNA, 0x01); + max2165_write_reg(priv, REG_PLL_CFG, 0x7A); + max2165_write_reg(priv, REG_TEST, 0x08); + max2165_write_reg(priv, REG_SHUTDOWN, 0x40); + max2165_write_reg(priv, REG_VCO_CTRL, 0x84); + max2165_write_reg(priv, REG_BASEBAND_CTRL, 0xC3); + max2165_write_reg(priv, REG_DC_OFFSET_CTRL, 0x75); + max2165_write_reg(priv, REG_DC_OFFSET_DAC, 0x00); + max2165_write_reg(priv, REG_ROM_TABLE_ADDR, 0x00); + + max2165_set_osc(priv, priv->config->osc_clk); + + max2165_read_rom_table(priv); + + max2165_set_bandwidth(priv, BANDWIDTH_8_MHZ); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + + return 0; +} + +static int max2165_release(struct dvb_frontend *fe) +{ + struct max2165_priv *priv = fe->tuner_priv; + dprintk("%s()\n", __func__); + + kfree(priv); + fe->tuner_priv = NULL; + + return 0; +} + +static const struct dvb_tuner_ops max2165_tuner_ops = { + .info = { + .name = "Maxim MAX2165", + .frequency_min = 470000000, + .frequency_max = 780000000, + .frequency_step = 50000, + }, + + .release = max2165_release, + .init = max2165_init, + .sleep = max2165_sleep, + + .set_params = max2165_set_params, + .set_analog_params = NULL, + .get_frequency = max2165_get_frequency, + .get_bandwidth = max2165_get_bandwidth, + .get_status = max2165_get_status +}; + +struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + struct max2165_priv *priv = NULL; + + dprintk("%s(%d-%04x)\n", __func__, + i2c ? i2c_adapter_id(i2c) : -1, + cfg ? cfg->i2c_address : -1); + + priv = kzalloc(sizeof(struct max2165_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + memcpy(&fe->ops.tuner_ops, &max2165_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + priv->config = cfg; + priv->i2c = i2c; + fe->tuner_priv = priv; + + max2165_init(fe); + max2165_debug_status(priv); + + return fe; +} +EXPORT_SYMBOL(max2165_attach); + +MODULE_AUTHOR("David T. L. Wong "); +MODULE_DESCRIPTION("Maxim MAX2165 silicon tuner driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/max2165.h b/drivers/media/common/tuners/max2165.h new file mode 100644 index 0000000000000..c063c36a93d3f --- /dev/null +++ b/drivers/media/common/tuners/max2165.h @@ -0,0 +1,48 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MAX2165_H__ +#define __MAX2165_H__ + +struct dvb_frontend; +struct i2c_adapter; + +struct max2165_config { + u8 i2c_address; + u8 osc_clk; /* in MHz, selectable values: 4,16,18,20,22,24,26,28 */ +}; + +#if defined(CONFIG_MEDIA_TUNER_MAX2165) || \ + (defined(CONFIG_MEDIA_TUNER_MAX2165_MODULE) && defined(MODULE)) +extern struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg); +#else +static inline struct dvb_frontend *max2165_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + struct max2165_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/max2165_priv.h b/drivers/media/common/tuners/max2165_priv.h new file mode 100644 index 0000000000000..91bbe021a08d3 --- /dev/null +++ b/drivers/media/common/tuners/max2165_priv.h @@ -0,0 +1,60 @@ +/* + * Driver for Maxim MAX2165 silicon tuner + * + * Copyright (c) 2009 David T. L. Wong + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MAX2165_PRIV_H__ +#define __MAX2165_PRIV_H__ + +#define REG_NDIV_INT 0x00 +#define REG_NDIV_FRAC2 0x01 +#define REG_NDIV_FRAC1 0x02 +#define REG_NDIV_FRAC0 0x03 +#define REG_TRACK_FILTER 0x04 +#define REG_LNA 0x05 +#define REG_PLL_CFG 0x06 +#define REG_TEST 0x07 +#define REG_SHUTDOWN 0x08 +#define REG_VCO_CTRL 0x09 +#define REG_BASEBAND_CTRL 0x0A +#define REG_DC_OFFSET_CTRL 0x0B +#define REG_DC_OFFSET_DAC 0x0C +#define REG_ROM_TABLE_ADDR 0x0D + +/* Read Only Registers */ +#define REG_ROM_TABLE_DATA 0x10 +#define REG_STATUS 0x11 +#define REG_AUTOTUNE 0x12 + +struct max2165_priv { + struct max2165_config *config; + struct i2c_adapter *i2c; + + u32 frequency; + u32 bandwidth; + + u8 tf_ntch_low_cfg; + u8 tf_ntch_hi_cfg; + u8 tf_balun_low_ref; + u8 tf_balun_hi_ref; + u8 bb_filter_7mhz_cfg; + u8 bb_filter_8mhz_cfg; +}; + +#endif diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c index 0803dab58fff1..605e28b732636 100644 --- a/drivers/media/common/tuners/mxl5005s.c +++ b/drivers/media/common/tuners/mxl5005s.c @@ -2789,7 +2789,10 @@ static u16 MXL_TuneRF(struct dvb_frontend *fe, u32 RF_Freq) /* add for 2.6.5 Special setting for QAM */ if (state->Mod_Type == MXL_QAM) { - if (state->RF_IN < 680000000) + if (state->config->qam_gain != 0) + status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, + state->config->qam_gain); + else if (state->RF_IN < 680000000) status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 3); else status += MXL_ControlWrite(fe, RFSYN_CHP_GAIN, 2); diff --git a/drivers/media/common/tuners/mxl5005s.h b/drivers/media/common/tuners/mxl5005s.h index 7ac6815b30aaf..fc8a1ffc53b4c 100644 --- a/drivers/media/common/tuners/mxl5005s.h +++ b/drivers/media/common/tuners/mxl5005s.h @@ -108,6 +108,10 @@ struct mxl5005s_config { #define MXL_LOW_IF 1 u8 if_mode; + /* Some boards need to override the built-in logic for determining + the gain when in QAM mode (the HVR-1600 is one such case) */ + u8 qam_gain; + /* Stuff I don't know what to do with */ u8 AgcMasterByte; }; diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c index 2d02698d4f4fd..7eb1bf75cd072 100644 --- a/drivers/media/common/tuners/mxl5007t.c +++ b/drivers/media/common/tuners/mxl5007t.c @@ -196,7 +196,7 @@ static void copy_reg_bits(struct reg_pair_t *reg_pair1, i = j = 0; while (reg_pair1[i].reg || reg_pair1[i].val) { - while (reg_pair2[j].reg || reg_pair2[j].reg) { + while (reg_pair2[j].reg || reg_pair2[j].val) { if (reg_pair1[i].reg != reg_pair2[j].reg) { j++; continue; diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c index 155c93eb75dad..e1f678281a58d 100644 --- a/drivers/media/common/tuners/tda18271-common.c +++ b/drivers/media/common/tuners/tda18271-common.c @@ -326,12 +326,24 @@ int tda18271_init_regs(struct dvb_frontend *fe) regs[R_EB22] = 0x48; regs[R_EB23] = 0xb0; - if (priv->small_i2c) { + switch (priv->small_i2c) { + case TDA18271_08_BYTE_CHUNK_INIT: + tda18271_write_regs(fe, 0x00, 0x08); + tda18271_write_regs(fe, 0x08, 0x08); + tda18271_write_regs(fe, 0x10, 0x08); + tda18271_write_regs(fe, 0x18, 0x08); + tda18271_write_regs(fe, 0x20, 0x07); + break; + case TDA18271_16_BYTE_CHUNK_INIT: tda18271_write_regs(fe, 0x00, 0x10); tda18271_write_regs(fe, 0x10, 0x10); tda18271_write_regs(fe, 0x20, 0x07); - } else + break; + case TDA18271_39_BYTE_CHUNK_INIT: + default: tda18271_write_regs(fe, 0x00, TDA18271_NUM_REGS); + break; + } /* setup agc1 gain */ regs[R_EB17] = 0x00; diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c index 3a50ce96fcb9a..b2e15456d5f36 100644 --- a/drivers/media/common/tuners/tda18271-fe.c +++ b/drivers/media/common/tuners/tda18271-fe.c @@ -256,8 +256,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, struct tda18271_priv *priv = fe->tuner_priv; struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; - int tm_current, rfcal_comp, approx, i, ret; - u8 dc_over_dt, rf_tab; + int i, ret; + u8 tm_current, dc_over_dt, rf_tab; + s32 rfcal_comp, approx; /* power up */ ret = tda18271_set_standby_mode(fe, 0, 0, 0); @@ -277,11 +278,11 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, return i; if ((0 == map[i].rf3) || (freq / 1000 < map[i].rf2)) { - approx = map[i].rf_a1 * - (freq / 1000 - map[i].rf1) + map[i].rf_b1 + rf_tab; + approx = map[i].rf_a1 * (s32)(freq / 1000 - map[i].rf1) + + map[i].rf_b1 + rf_tab; } else { - approx = map[i].rf_a2 * - (freq / 1000 - map[i].rf2) + map[i].rf_b2 + rf_tab; + approx = map[i].rf_a2 * (s32)(freq / 1000 - map[i].rf2) + + map[i].rf_b2 + rf_tab; } if (approx < 0) @@ -292,9 +293,9 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); /* calculate temperature compensation */ - rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000; + rfcal_comp = dc_over_dt * (s32)(tm_current - priv->tm_rfcal) / 1000; - regs[R_EB14] = approx + rfcal_comp; + regs[R_EB14] = (unsigned char)(approx + rfcal_comp); ret = tda18271_write_regs(fe, R_EB14, 1); fail: return ret; @@ -572,6 +573,7 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) struct tda18271_rf_tracking_filter_cal *map = priv->rf_cal_state; unsigned char *regs = priv->tda18271_regs; int bcal, rf, i; + s32 divisor, dividend; #define RF1 0 #define RF2 1 #define RF3 2 @@ -610,20 +612,22 @@ static int tda18271_rf_tracking_filters_init(struct dvb_frontend *fe, u32 freq) switch (rf) { case RF1: map[i].rf_a1 = 0; - map[i].rf_b1 = prog_cal[RF1] - prog_tab[RF1]; + map[i].rf_b1 = (s32)(prog_cal[RF1] - prog_tab[RF1]); map[i].rf1 = rf_freq[RF1] / 1000; break; case RF2: - map[i].rf_a1 = (prog_cal[RF2] - prog_tab[RF2] - - prog_cal[RF1] + prog_tab[RF1]) / - (s32)((rf_freq[RF2] - rf_freq[RF1]) / 1000); + dividend = (s32)(prog_cal[RF2] - prog_tab[RF2]) - + (s32)(prog_cal[RF1] + prog_tab[RF1]); + divisor = (s32)(rf_freq[RF2] - rf_freq[RF1]) / 1000; + map[i].rf_a1 = (dividend / divisor); map[i].rf2 = rf_freq[RF2] / 1000; break; case RF3: - map[i].rf_a2 = (prog_cal[RF3] - prog_tab[RF3] - - prog_cal[RF2] + prog_tab[RF2]) / - (s32)((rf_freq[RF3] - rf_freq[RF2]) / 1000); - map[i].rf_b2 = prog_cal[RF2] - prog_tab[RF2]; + dividend = (s32)(prog_cal[RF3] - prog_tab[RF3]) - + (s32)(prog_cal[RF2] + prog_tab[RF2]); + divisor = (s32)(rf_freq[RF3] - rf_freq[RF2]) / 1000; + map[i].rf_a2 = (dividend / divisor); + map[i].rf_b2 = (s32)(prog_cal[RF2] - prog_tab[RF2]); map[i].rf3 = rf_freq[RF3] / 1000; break; default: @@ -1181,6 +1185,48 @@ static int tda18271_get_id(struct dvb_frontend *fe) return ret; } +static int tda18271_setup_configuration(struct dvb_frontend *fe, + struct tda18271_config *cfg) +{ + struct tda18271_priv *priv = fe->tuner_priv; + + priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; + priv->role = (cfg) ? cfg->role : TDA18271_MASTER; + priv->config = (cfg) ? cfg->config : 0; + priv->small_i2c = (cfg) ? + cfg->small_i2c : TDA18271_39_BYTE_CHUNK_INIT; + priv->output_opt = (cfg) ? + cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; + + return 0; +} + +static inline int tda18271_need_cal_on_startup(struct tda18271_config *cfg) +{ + /* tda18271_cal_on_startup == -1 when cal module option is unset */ + return ((tda18271_cal_on_startup == -1) ? + /* honor configuration setting */ + ((cfg) && (cfg->rf_cal_on_startup)) : + /* module option overrides configuration setting */ + (tda18271_cal_on_startup)) ? 1 : 0; +} + +static int tda18271_set_config(struct dvb_frontend *fe, void *priv_cfg) +{ + struct tda18271_config *cfg = (struct tda18271_config *) priv_cfg; + + tda18271_setup_configuration(fe, cfg); + + if (tda18271_need_cal_on_startup(cfg)) + tda18271_init(fe); + + /* override default std map with values in config struct */ + if ((cfg) && (cfg->std_map)) + tda18271_update_std_map(fe, cfg->std_map); + + return 0; +} + static struct dvb_tuner_ops tda18271_tuner_ops = { .info = { .name = "NXP TDA18271HD", @@ -1193,6 +1239,7 @@ static struct dvb_tuner_ops tda18271_tuner_ops = { .set_params = tda18271_set_params, .set_analog_params = tda18271_set_analog_params, .release = tda18271_release, + .set_config = tda18271_set_config, .get_frequency = tda18271_get_frequency, .get_bandwidth = tda18271_get_bandwidth, }; @@ -1213,33 +1260,14 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, case 0: goto fail; case 1: - { /* new tuner instance */ - int rf_cal_on_startup; - - priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; - priv->role = (cfg) ? cfg->role : TDA18271_MASTER; - priv->config = (cfg) ? cfg->config : 0; - priv->small_i2c = (cfg) ? cfg->small_i2c : 0; - priv->output_opt = (cfg) ? - cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; - - /* tda18271_cal_on_startup == -1 when cal - * module option is unset */ - if (tda18271_cal_on_startup == -1) { - /* honor attach-time configuration */ - rf_cal_on_startup = - ((cfg) && (cfg->rf_cal_on_startup)) ? 1 : 0; - } else { - /* module option overrides attach configuration */ - rf_cal_on_startup = tda18271_cal_on_startup; - } + fe->tuner_priv = priv; + + tda18271_setup_configuration(fe, cfg); priv->cal_initialized = false; mutex_init(&priv->lock); - fe->tuner_priv = priv; - if (tda_fail(tda18271_get_id(fe))) goto fail; @@ -1249,12 +1277,12 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, mutex_lock(&priv->lock); tda18271_init_regs(fe); - if ((rf_cal_on_startup) && (priv->id == TDA18271HDC2)) + if ((tda18271_need_cal_on_startup(cfg)) && + (priv->id == TDA18271HDC2)) tda18271c2_rf_cal_init(fe); mutex_unlock(&priv->lock); break; - } default: /* existing tuner instance */ fe->tuner_priv = priv; @@ -1271,7 +1299,11 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->small_i2c = cfg->small_i2c; if (cfg->output_opt) priv->output_opt = cfg->output_opt; + if (cfg->std_map) + tda18271_update_std_map(fe, cfg->std_map); } + if (tda18271_need_cal_on_startup(cfg)) + tda18271_init(fe); break; } @@ -1298,7 +1330,7 @@ EXPORT_SYMBOL_GPL(tda18271_attach); MODULE_DESCRIPTION("NXP TDA18271HD analog / digital tuner driver"); MODULE_AUTHOR("Michael Krufky "); MODULE_LICENSE("GPL"); -MODULE_VERSION("0.3"); +MODULE_VERSION("0.4"); /* * Overrides for Emacs so that we follow Linus's tabbing style. diff --git a/drivers/media/common/tuners/tda18271-maps.c b/drivers/media/common/tuners/tda18271-maps.c index e21fdeff3ddf9..e7f84c705da86 100644 --- a/drivers/media/common/tuners/tda18271-maps.c +++ b/drivers/media/common/tuners/tda18271-maps.c @@ -978,6 +978,7 @@ static struct tda18271_cid_target_map tda18271_cid_target[] = { int tda18271_lookup_cid_target(struct dvb_frontend *fe, u32 *freq, u8 *cid_target, u16 *count_limit) { + struct tda18271_priv *priv = fe->tuner_priv; int i = 0; while ((tda18271_cid_target[i].rfmax * 1000) < *freq) { diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h index 2bee229acd917..9589ab0576d2b 100644 --- a/drivers/media/common/tuners/tda18271-priv.h +++ b/drivers/media/common/tuners/tda18271-priv.h @@ -80,10 +80,10 @@ struct tda18271_rf_tracking_filter_cal { u32 rf1; u32 rf2; u32 rf3; - int rf_a1; - int rf_b1; - int rf_a2; - int rf_b2; + s32 rf_a1; + s32 rf_b1; + s32 rf_a2; + s32 rf_b2; }; enum tda18271_pll { @@ -109,11 +109,12 @@ struct tda18271_priv { enum tda18271_i2c_gate gate; enum tda18271_ver id; enum tda18271_output_options output_opt; + enum tda18271_small_i2c small_i2c; unsigned int config; /* interface to saa713x / tda829x */ - unsigned int tm_rfcal; unsigned int cal_initialized:1; - unsigned int small_i2c:1; + + u8 tm_rfcal; struct tda18271_map_layout *maps; struct tda18271_std_map std; @@ -135,27 +136,37 @@ extern int tda18271_debug; #define DBG_ADV 8 #define DBG_CAL 16 -#define tda_printk(kern, fmt, arg...) \ - printk(kern "%s: " fmt, __func__, ##arg) - -#define tda_dprintk(lvl, fmt, arg...) do {\ +#define tda_printk(st, kern, fmt, arg...) do {\ + if (st) { \ + struct tda18271_priv *state = st; \ + printk(kern "%s: [%d-%04x|%s] " fmt, __func__, \ + i2c_adapter_id(state->i2c_props.adap), \ + state->i2c_props.addr, \ + (state->role == TDA18271_MASTER) \ + ? "M" : "S", ##arg); \ + } else \ + printk(kern "%s: " fmt, __func__, ##arg); \ +} while (0) + +#define tda_dprintk(st, lvl, fmt, arg...) do {\ if (tda18271_debug & lvl) \ - tda_printk(KERN_DEBUG, fmt, ##arg); } while (0) + tda_printk(st, KERN_DEBUG, fmt, ##arg); } while (0) #define tda_info(fmt, arg...) printk(KERN_INFO fmt, ##arg) -#define tda_warn(fmt, arg...) tda_printk(KERN_WARNING, fmt, ##arg) -#define tda_err(fmt, arg...) tda_printk(KERN_ERR, fmt, ##arg) -#define tda_dbg(fmt, arg...) tda_dprintk(DBG_INFO, fmt, ##arg) -#define tda_map(fmt, arg...) tda_dprintk(DBG_MAP, fmt, ##arg) -#define tda_reg(fmt, arg...) tda_dprintk(DBG_REG, fmt, ##arg) -#define tda_cal(fmt, arg...) tda_dprintk(DBG_CAL, fmt, ##arg) +#define tda_warn(fmt, arg...) tda_printk(priv, KERN_WARNING, fmt, ##arg) +#define tda_err(fmt, arg...) tda_printk(priv, KERN_ERR, fmt, ##arg) +#define tda_dbg(fmt, arg...) tda_dprintk(priv, DBG_INFO, fmt, ##arg) +#define tda_map(fmt, arg...) tda_dprintk(priv, DBG_MAP, fmt, ##arg) +#define tda_reg(fmt, arg...) tda_dprintk(priv, DBG_REG, fmt, ##arg) +#define tda_cal(fmt, arg...) tda_dprintk(priv, DBG_CAL, fmt, ##arg) #define tda_fail(ret) \ ({ \ int __ret; \ __ret = (ret < 0); \ if (__ret) \ - tda_printk(KERN_ERR, "error %d on line %d\n", ret, __LINE__);\ + tda_printk(priv, KERN_ERR, \ + "error %d on line %d\n", ret, __LINE__); \ __ret; \ }) diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h index 323f2912128da..d7fcc36dc6e6a 100644 --- a/drivers/media/common/tuners/tda18271.h +++ b/drivers/media/common/tuners/tda18271.h @@ -78,6 +78,12 @@ enum tda18271_output_options { TDA18271_OUTPUT_XT_OFF = 2, }; +enum tda18271_small_i2c { + TDA18271_39_BYTE_CHUNK_INIT = 0, + TDA18271_16_BYTE_CHUNK_INIT = 1, + TDA18271_08_BYTE_CHUNK_INIT = 2, +}; + struct tda18271_config { /* override default if freq / std settings (optional) */ struct tda18271_std_map *std_map; @@ -91,12 +97,12 @@ struct tda18271_config { /* output options that can be disabled */ enum tda18271_output_options output_opt; + /* some i2c providers cant write all 39 registers at once */ + enum tda18271_small_i2c small_i2c; + /* force rf tracking filter calibration on startup */ unsigned int rf_cal_on_startup:1; - /* some i2c providers cant write all 39 registers at once */ - unsigned int small_i2c:1; - /* interface to saa713x / tda829x */ unsigned int config; }; diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c index 064d14c8d7b29..c190b0dedee47 100644 --- a/drivers/media/common/tuners/tda8290.c +++ b/drivers/media/common/tuners/tda8290.c @@ -33,6 +33,7 @@ module_param(debug, int, 0644); MODULE_PARM_DESC(debug, "enable verbose debug messages"); static int deemphasis_50; +module_param(deemphasis_50, int, 0644); MODULE_PARM_DESC(deemphasis_50, "0 - 75us deemphasis; 1 - 50us deemphasis"); /* ---------------------------------------------------------------------- */ diff --git a/drivers/media/common/tuners/tda9887.c b/drivers/media/common/tuners/tda9887.c index 544cdbe88a6c9..a71c100c95df6 100644 --- a/drivers/media/common/tuners/tda9887.c +++ b/drivers/media/common/tuners/tda9887.c @@ -463,7 +463,7 @@ static int tda9887_set_insmod(struct dvb_frontend *fe) buf[1] &= ~cQSS; } - if (adjust >= 0x00 && adjust < 0x20) { + if (adjust < 0x20) { buf[2] &= ~cTopMask; buf[2] |= adjust; } diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index f4ffcdc9b8489..432003dded7cc 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -61,6 +61,7 @@ struct xc5000_priv { u32 bandwidth; u8 video_standard; u8 rf_mode; + u8 radio_input; }; /* Misc Defines */ @@ -632,8 +633,12 @@ static int xc5000_set_params(struct dvb_frontend *fe, struct xc5000_priv *priv = fe->tuner_priv; int ret; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { + dprintk(1, "Unable to load firmware and init tuner\n"); + return -EINVAL; + } + } dprintk(1, "%s() frequency=%d (Hz)\n", __func__, params->frequency); @@ -739,15 +744,12 @@ static int xc5000_is_firmware_loaded(struct dvb_frontend *fe) return ret; } -static int xc5000_set_analog_params(struct dvb_frontend *fe, +static int xc5000_set_tv_freq(struct dvb_frontend *fe, struct analog_parameters *params) { struct xc5000_priv *priv = fe->tuner_priv; int ret; - if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) - xc_load_fw_and_init_tuner(fe); - dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n", __func__, params->frequency); @@ -827,6 +829,86 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe, return 0; } +static int xc5000_set_radio_freq(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + u8 radio_input; + + dprintk(1, "%s() frequency=%d (in units of khz)\n", + __func__, params->frequency); + + if (priv->radio_input == XC5000_RADIO_NOT_CONFIGURED) { + dprintk(1, "%s() radio input not configured\n", __func__); + return -EINVAL; + } + + if (priv->radio_input == XC5000_RADIO_FM1) + radio_input = FM_Radio_INPUT1; + else if (priv->radio_input == XC5000_RADIO_FM2) + radio_input = FM_Radio_INPUT2; + else { + dprintk(1, "%s() unknown radio input %d\n", __func__, + priv->radio_input); + return -EINVAL; + } + + priv->freq_hz = params->frequency * 125 / 2; + + priv->rf_mode = XC_RF_MODE_AIR; + + ret = xc_SetTVStandard(priv, XC5000_Standard[radio_input].VideoMode, + XC5000_Standard[radio_input].AudioMode); + + if (ret != XC_RESULT_SUCCESS) { + printk(KERN_ERR "xc5000: xc_SetTVStandard failed\n"); + return -EREMOTEIO; + } + + ret = xc_SetSignalSource(priv, priv->rf_mode); + if (ret != XC_RESULT_SUCCESS) { + printk(KERN_ERR + "xc5000: xc_SetSignalSource(%d) failed\n", + priv->rf_mode); + return -EREMOTEIO; + } + + xc_tune_channel(priv, priv->freq_hz, XC_TUNE_ANALOG); + + return 0; +} + +static int xc5000_set_analog_params(struct dvb_frontend *fe, + struct analog_parameters *params) +{ + struct xc5000_priv *priv = fe->tuner_priv; + int ret = -EINVAL; + + if (priv->i2c_props.adap == NULL) + return -EINVAL; + + if (xc5000_is_firmware_loaded(fe) != XC_RESULT_SUCCESS) { + if (xc_load_fw_and_init_tuner(fe) != XC_RESULT_SUCCESS) { + dprintk(1, "Unable to load firmware and init tuner\n"); + return -EINVAL; + } + } + + switch (params->mode) { + case V4L2_TUNER_RADIO: + ret = xc5000_set_radio_freq(fe, params); + break; + case V4L2_TUNER_ANALOG_TV: + case V4L2_TUNER_DIGITAL_TV: + ret = xc5000_set_tv_freq(fe, params); + break; + } + + return ret; +} + + static int xc5000_get_frequency(struct dvb_frontend *fe, u32 *freq) { struct xc5000_priv *priv = fe->tuner_priv; @@ -1000,6 +1082,9 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe, priv->if_khz = cfg->if_khz; } + if (priv->radio_input == 0) + priv->radio_input = cfg->radio_input; + /* Check if firmware has been loaded. It is possible that another instance of the driver has loaded the firmware. */ diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index f4c146698a005..e6d7236c9ea14 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -30,11 +30,17 @@ struct i2c_adapter; struct xc5000_config { u8 i2c_address; u32 if_khz; + u8 radio_input; }; /* xc5000 callback command */ #define XC5000_TUNER_RESET 0 +/* Possible Radio inputs */ +#define XC5000_RADIO_NOT_CONFIGURED 0 +#define XC5000_RADIO_FM1 1 +#define XC5000_RADIO_FM2 2 + /* For each bridge framework, when it attaches either analog or digital, * it has to store a reference back to its _core equivalent structure, * so that it can service the hardware by steering gpio's etc. diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c index b1857c19bbd2c..78fc469f0f697 100644 --- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c +++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c @@ -215,7 +215,7 @@ static int cx24108_tuner_set_params(struct dvb_frontend* fe, struct dvb_frontend freq = 2150000; /* satellite IF is 950..2150MHz */ /* decide which VCO to use for the input frequency */ - for(i = 1; (i < ARRAY_SIZE(osci)) && (osci[i] < freq); i++); + for(i = 1; (i < ARRAY_SIZE(osci) - 1) && (osci[i] < freq); i++); printk("cx24108 debug: select vco #%d (f=%d)\n",i,freq); band=bandsel[i]; /* the gain values must be set by SetSymbolrate */ diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index 2d099e2717519..53e3f2a7d31aa 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -510,7 +510,7 @@ static void dm1105_emit_key(struct work_struct *work) data = (ircom >> 8) & 0x7f; - ir_input_keydown(ir->input_dev, &ir->ir, data, data); + ir_input_keydown(ir->input_dev, &ir->ir, data); ir_input_nokey(ir->input_dev, &ir->ir); } @@ -589,7 +589,12 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys), "pci-%s/ir0", pci_name(dm1105->pdev)); - ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes); + if (err < 0) { + input_free_device(input_dev); + return err; + } + input_dev->name = "DVB on-card IR receiver"; input_dev->phys = dm1105->ir.input_phys; input_dev->id.bustype = BUS_PCI; @@ -608,6 +613,7 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) err = input_register_device(input_dev); if (err) { + ir_input_free(input_dev); input_free_device(input_dev); return err; } @@ -617,8 +623,8 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105) void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105) { + ir_input_free(dm1105->ir.input_dev); input_unregister_device(dm1105->ir.input_dev); - } static int __devinit dm1105dvb_hw_init(struct dm1105dvb *dm1105dvb) diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index 91c537bca8ada..b78cfb7d1897d 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -30,6 +30,7 @@ #include #include #include +#include #include "dvb_demux.h" @@ -44,6 +45,11 @@ module_param(dvb_demux_tscheck, int, 0644); MODULE_PARM_DESC(dvb_demux_tscheck, "enable transport stream continuity and TEI check"); +static int dvb_demux_speedcheck; +module_param(dvb_demux_speedcheck, int, 0644); +MODULE_PARM_DESC(dvb_demux_speedcheck, + "enable transport stream speed check"); + #define dprintk_tscheck(x...) do { \ if (dvb_demux_tscheck && printk_ratelimit()) \ printk(x); \ @@ -387,6 +393,39 @@ static void dvb_dmx_swfilter_packet(struct dvb_demux *demux, const u8 *buf) u16 pid = ts_pid(buf); int dvr_done = 0; + if (dvb_demux_speedcheck) { + struct timespec cur_time, delta_time; + u64 speed_bytes, speed_timedelta; + + demux->speed_pkts_cnt++; + + /* show speed every SPEED_PKTS_INTERVAL packets */ + if (!(demux->speed_pkts_cnt % SPEED_PKTS_INTERVAL)) { + cur_time = current_kernel_time(); + + if (demux->speed_last_time.tv_sec != 0 && + demux->speed_last_time.tv_nsec != 0) { + delta_time = timespec_sub(cur_time, + demux->speed_last_time); + speed_bytes = (u64)demux->speed_pkts_cnt + * 188 * 8; + /* convert to 1024 basis */ + speed_bytes = 1000 * div64_u64(speed_bytes, + 1024); + speed_timedelta = + (u64)timespec_to_ns(&delta_time); + speed_timedelta = div64_u64(speed_timedelta, + 1000000); /* nsec -> usec */ + printk(KERN_INFO "TS speed %llu Kbits/sec \n", + div64_u64(speed_bytes, + speed_timedelta)); + }; + + demux->speed_last_time = cur_time; + demux->speed_pkts_cnt = 0; + }; + }; + if (dvb_demux_tscheck) { if (!demux->cnt_storage) demux->cnt_storage = vmalloc(MAX_PID + 1); diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h index 2fe05d03240d7..a7d876fd02dd8 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.h +++ b/drivers/media/dvb/dvb-core/dvb_demux.h @@ -44,6 +44,8 @@ #define MAX_PID 0x1fff +#define SPEED_PKTS_INTERVAL 50000 + struct dvb_demux_filter { struct dmx_section_filter filter; u8 maskandmode[DMX_MAX_FILTER_SIZE]; @@ -131,6 +133,9 @@ struct dvb_demux { spinlock_t lock; uint8_t *cnt_storage; /* for TS continuity check */ + + struct timespec speed_last_time; /* for TS speed check */ + uint32_t speed_pkts_cnt; /* for TS speed check */ }; int dvb_dmx_init(struct dvb_demux *dvbdemux); diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index 98082416aa52a..07461222a7f50 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -895,104 +895,27 @@ static int dvb_frontend_clear_cache(struct dvb_frontend *fe) } static struct dtv_cmds_h dtv_cmds[] = { - [DTV_TUNE] = { - .name = "DTV_TUNE", - .cmd = DTV_TUNE, - .set = 1, - }, - [DTV_CLEAR] = { - .name = "DTV_CLEAR", - .cmd = DTV_CLEAR, - .set = 1, - }, + _DTV_CMD(DTV_TUNE, 1, 0), + _DTV_CMD(DTV_CLEAR, 1, 0), /* Set */ - [DTV_FREQUENCY] = { - .name = "DTV_FREQUENCY", - .cmd = DTV_FREQUENCY, - .set = 1, - }, - [DTV_BANDWIDTH_HZ] = { - .name = "DTV_BANDWIDTH_HZ", - .cmd = DTV_BANDWIDTH_HZ, - .set = 1, - }, - [DTV_MODULATION] = { - .name = "DTV_MODULATION", - .cmd = DTV_MODULATION, - .set = 1, - }, - [DTV_INVERSION] = { - .name = "DTV_INVERSION", - .cmd = DTV_INVERSION, - .set = 1, - }, - [DTV_DISEQC_MASTER] = { - .name = "DTV_DISEQC_MASTER", - .cmd = DTV_DISEQC_MASTER, - .set = 1, - .buffer = 1, - }, - [DTV_SYMBOL_RATE] = { - .name = "DTV_SYMBOL_RATE", - .cmd = DTV_SYMBOL_RATE, - .set = 1, - }, - [DTV_INNER_FEC] = { - .name = "DTV_INNER_FEC", - .cmd = DTV_INNER_FEC, - .set = 1, - }, - [DTV_VOLTAGE] = { - .name = "DTV_VOLTAGE", - .cmd = DTV_VOLTAGE, - .set = 1, - }, - [DTV_TONE] = { - .name = "DTV_TONE", - .cmd = DTV_TONE, - .set = 1, - }, - [DTV_PILOT] = { - .name = "DTV_PILOT", - .cmd = DTV_PILOT, - .set = 1, - }, - [DTV_ROLLOFF] = { - .name = "DTV_ROLLOFF", - .cmd = DTV_ROLLOFF, - .set = 1, - }, - [DTV_DELIVERY_SYSTEM] = { - .name = "DTV_DELIVERY_SYSTEM", - .cmd = DTV_DELIVERY_SYSTEM, - .set = 1, - }, - [DTV_HIERARCHY] = { - .name = "DTV_HIERARCHY", - .cmd = DTV_HIERARCHY, - .set = 1, - }, - [DTV_CODE_RATE_HP] = { - .name = "DTV_CODE_RATE_HP", - .cmd = DTV_CODE_RATE_HP, - .set = 1, - }, - [DTV_CODE_RATE_LP] = { - .name = "DTV_CODE_RATE_LP", - .cmd = DTV_CODE_RATE_LP, - .set = 1, - }, - [DTV_GUARD_INTERVAL] = { - .name = "DTV_GUARD_INTERVAL", - .cmd = DTV_GUARD_INTERVAL, - .set = 1, - }, - [DTV_TRANSMISSION_MODE] = { - .name = "DTV_TRANSMISSION_MODE", - .cmd = DTV_TRANSMISSION_MODE, - .set = 1, - }, + _DTV_CMD(DTV_FREQUENCY, 1, 0), + _DTV_CMD(DTV_BANDWIDTH_HZ, 1, 0), + _DTV_CMD(DTV_MODULATION, 1, 0), + _DTV_CMD(DTV_INVERSION, 1, 0), + _DTV_CMD(DTV_DISEQC_MASTER, 1, 1), + _DTV_CMD(DTV_SYMBOL_RATE, 1, 0), + _DTV_CMD(DTV_INNER_FEC, 1, 0), + _DTV_CMD(DTV_VOLTAGE, 1, 0), + _DTV_CMD(DTV_TONE, 1, 0), + _DTV_CMD(DTV_PILOT, 1, 0), + _DTV_CMD(DTV_ROLLOFF, 1, 0), + _DTV_CMD(DTV_DELIVERY_SYSTEM, 1, 0), + _DTV_CMD(DTV_HIERARCHY, 1, 0), + _DTV_CMD(DTV_CODE_RATE_HP, 1, 0), + _DTV_CMD(DTV_CODE_RATE_LP, 1, 0), + _DTV_CMD(DTV_GUARD_INTERVAL, 1, 0), + _DTV_CMD(DTV_TRANSMISSION_MODE, 1, 0), _DTV_CMD(DTV_ISDBT_PARTIAL_RECEPTION, 1, 0), _DTV_CMD(DTV_ISDBT_SOUND_BROADCASTING, 1, 0), @@ -1035,43 +958,13 @@ static struct dtv_cmds_h dtv_cmds[] = { _DTV_CMD(DTV_ISDBS_TS_ID, 1, 0), /* Get */ - [DTV_DISEQC_SLAVE_REPLY] = { - .name = "DTV_DISEQC_SLAVE_REPLY", - .cmd = DTV_DISEQC_SLAVE_REPLY, - .set = 0, - .buffer = 1, - }, - - [DTV_API_VERSION] = { - .name = "DTV_API_VERSION", - .cmd = DTV_API_VERSION, - .set = 0, - }, - [DTV_CODE_RATE_HP] = { - .name = "DTV_CODE_RATE_HP", - .cmd = DTV_CODE_RATE_HP, - .set = 0, - }, - [DTV_CODE_RATE_LP] = { - .name = "DTV_CODE_RATE_LP", - .cmd = DTV_CODE_RATE_LP, - .set = 0, - }, - [DTV_GUARD_INTERVAL] = { - .name = "DTV_GUARD_INTERVAL", - .cmd = DTV_GUARD_INTERVAL, - .set = 0, - }, - [DTV_TRANSMISSION_MODE] = { - .name = "DTV_TRANSMISSION_MODE", - .cmd = DTV_TRANSMISSION_MODE, - .set = 0, - }, - [DTV_HIERARCHY] = { - .name = "DTV_HIERARCHY", - .cmd = DTV_HIERARCHY, - .set = 0, - }, + _DTV_CMD(DTV_DISEQC_SLAVE_REPLY, 0, 1), + _DTV_CMD(DTV_API_VERSION, 0, 0), + _DTV_CMD(DTV_CODE_RATE_HP, 0, 0), + _DTV_CMD(DTV_CODE_RATE_LP, 0, 0), + _DTV_CMD(DTV_GUARD_INTERVAL, 0, 0), + _DTV_CMD(DTV_TRANSMISSION_MODE, 0, 0), + _DTV_CMD(DTV_HIERARCHY, 0, 0), }; static void dtv_property_dump(struct dtv_property *tvp) @@ -1712,7 +1605,18 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, struct dvb_device *dvbdev = file->private_data; struct dvb_frontend *fe = dvbdev->priv; struct dvb_frontend_private *fepriv = fe->frontend_priv; - int err = -EOPNOTSUPP; + int cb_err, err = -EOPNOTSUPP; + + if (fe->dvb->fe_ioctl_override) { + cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg, + DVB_FE_IOCTL_PRE); + if (cb_err < 0) + return cb_err; + if (cb_err > 0) + return 0; + /* fe_ioctl_override returning 0 allows + * dvb-core to continue handling the ioctl */ + } switch (cmd) { case FE_GET_INFO: { @@ -1978,6 +1882,13 @@ static int dvb_frontend_ioctl_legacy(struct inode *inode, struct file *file, break; }; + if (fe->dvb->fe_ioctl_override) { + cb_err = fe->dvb->fe_ioctl_override(fe, cmd, parg, + DVB_FE_IOCTL_POST); + if (cb_err < 0) + return cb_err; + } + return err; } diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h index 01fc704847434..f7b499d4a3c0a 100644 --- a/drivers/media/dvb/dvb-core/dvbdev.h +++ b/drivers/media/dvb/dvb-core/dvbdev.h @@ -54,6 +54,8 @@ module_param_array(adapter_nr, short, NULL, 0444); \ MODULE_PARM_DESC(adapter_nr, "DVB adapter numbers") +struct dvb_frontend; + struct dvb_adapter { int num; struct list_head list_head; @@ -69,6 +71,32 @@ struct dvb_adapter { int mfe_shared; /* indicates mutually exclusive frontends */ struct dvb_device *mfe_dvbdev; /* frontend device in use */ struct mutex mfe_lock; /* access lock for thread creation */ + + /* Allow the adapter/bridge driver to perform an action before and/or + * after the core handles an ioctl: + * + * DVB_FE_IOCTL_PRE indicates that the ioctl has not yet been handled. + * DVB_FE_IOCTL_POST indicates that the ioctl has been handled. + * + * When DVB_FE_IOCTL_PRE is passed to the callback as the stage arg: + * + * return 0 to allow dvb-core to handle the ioctl. + * return a positive int to prevent dvb-core from handling the ioctl, + * and exit without error. + * return a negative int to prevent dvb-core from handling the ioctl, + * and return that value as an error. + * + * When DVB_FE_IOCTL_POST is passed to the callback as the stage arg: + * + * return 0 to allow the dvb_frontend ioctl handler to exit normally. + * return a negative int to cause the dvb_frontend ioctl handler to + * return that value as an error. + */ +#define DVB_FE_IOCTL_PRE 0 +#define DVB_FE_IOCTL_POST 1 + int (*fe_ioctl_override)(struct dvb_frontend *fe, + unsigned int cmd, void *parg, + unsigned int stage); }; diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 0e4b97fba384b..2dee1bf73577f 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -322,3 +322,11 @@ config DVB_USB_FRIIO depends on DVB_USB help Say Y here to support the Japanese DTV receiver Friio. + +config DVB_USB_EC168 + tristate "E3C EC168 DVB-T USB2.0 support" + depends on DVB_USB && EXPERIMENTAL + select DVB_EC100 + select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the E3C EC168 DVB-T USB2.0 receiver. diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index 85b83a43d55d6..72c92cb69a221 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -82,6 +82,9 @@ obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o dvb-usb-friio-objs = friio.o friio-fe.o obj-$(CONFIG_DVB_USB_FRIIO) += dvb-usb-friio.o +dvb-usb-ec168-objs = ec168.o +obj-$(CONFIG_DVB_USB_EC168) += dvb-usb-ec168.o + EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/ # due to tuner-xc3028 EXTRA_CFLAGS += -Idrivers/media/common/tuners diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index cf042b309b461..8b60a601fb82e 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -730,7 +730,7 @@ static int af9015_read_config(struct usb_device *udev) goto error; deb_info("%s: IR mode:%d\n", __func__, val); for (i = 0; i < af9015_properties_count; i++) { - if (val == AF9015_IR_MODE_DISABLED || val == 0x04) { + if (val == AF9015_IR_MODE_DISABLED) { af9015_properties[i].rc_key_map = NULL; af9015_properties[i].rc_key_map_size = 0; } else if (dvb_usb_af9015_remote) { @@ -868,6 +868,16 @@ static int af9015_read_config(struct usb_device *udev) af9015_config.ir_table_size = ARRAY_SIZE(af9015_ir_table_avermedia); break; + case USB_VID_MSI_2: + af9015_properties[i].rc_key_map = + af9015_rc_keys_msi_digivox_iii; + af9015_properties[i].rc_key_map_size = + ARRAY_SIZE(af9015_rc_keys_msi_digivox_iii); + af9015_config.ir_table = + af9015_ir_table_msi_digivox_iii; + af9015_config.ir_table_size = + ARRAY_SIZE(af9015_ir_table_msi_digivox_iii); + break; } } } @@ -1283,6 +1293,8 @@ static struct usb_device_id af9015_usb_table[] = { {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)}, {USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)}, /* 25 */{USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)}, + {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, {0}, }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1296,7 +1308,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .firmware = "dvb-usb-af9015.fw", .no_reconnect = 1, - .size_of_priv = sizeof(struct af9015_state), \ + .size_of_priv = sizeof(struct af9015_state), .num_adapters = 2, .adapter = { @@ -1402,7 +1414,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .firmware = "dvb-usb-af9015.fw", .no_reconnect = 1, - .size_of_priv = sizeof(struct af9015_state), \ + .size_of_priv = sizeof(struct af9015_state), .num_adapters = 2, .adapter = { @@ -1508,7 +1520,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .firmware = "dvb-usb-af9015.fw", .no_reconnect = 1, - .size_of_priv = sizeof(struct af9015_state), \ + .size_of_priv = sizeof(struct af9015_state), .num_adapters = 2, .adapter = { @@ -1554,7 +1566,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .i2c_algo = &af9015_i2c_algo, - .num_device_descs = 4, /* max 9 */ + .num_device_descs = 6, /* max 9 */ .devices = { { .name = "AverMedia AVerTV Volar GPS 805 (A805)", @@ -1577,6 +1589,17 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = {&af9015_usb_table[24], NULL}, .warm_ids = {NULL}, }, + { + .name = "KWorld PlusTV DVB-T PCI Pro Card " \ + "(DVB-T PC160-T)", + .cold_ids = {&af9015_usb_table[26], NULL}, + .warm_ids = {NULL}, + }, + { + .name = "Sveon STV20 Tuner USB DVB-T HDTV", + .cold_ids = {&af9015_usb_table[27], NULL}, + .warm_ids = {NULL}, + }, } }, }; diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h index c41f30e4a1b8c..931c8515830d6 100644 --- a/drivers/media/dvb/dvb-usb/af9015.h +++ b/drivers/media/dvb/dvb-usb/af9015.h @@ -95,6 +95,7 @@ enum af9015_ir_mode { AF9015_IR_MODE_HID, AF9015_IR_MODE_RLC, AF9015_IR_MODE_RC6, + AF9015_IR_MODE_POLLING, /* just guess */ }; struct af9015_state { @@ -119,6 +120,7 @@ enum af9015_remote { /* 5 */ AF9015_REMOTE_AVERMEDIA_KS, }; +/* LeadTek - Y04G0051 */ /* Leadtek WinFast DTV Dongle Gold */ static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = { { 0x001e, KEY_1 }, @@ -131,64 +133,96 @@ static struct dvb_usb_rc_key af9015_rc_keys_leadtek[] = { { 0x0025, KEY_8 }, { 0x0026, KEY_9 }, { 0x0027, KEY_0 }, - { 0x0028, KEY_ENTER }, - { 0x004f, KEY_VOLUMEUP }, - { 0x0050, KEY_VOLUMEDOWN }, - { 0x0051, KEY_CHANNELDOWN }, - { 0x0052, KEY_CHANNELUP }, + { 0x0028, KEY_OK }, + { 0x004f, KEY_RIGHT }, + { 0x0050, KEY_LEFT }, + { 0x0051, KEY_DOWN }, + { 0x0052, KEY_UP }, + { 0x011a, KEY_POWER2 }, + { 0x04b4, KEY_TV }, + { 0x04b3, KEY_RED }, + { 0x04b2, KEY_GREEN }, + { 0x04b1, KEY_YELLOW }, + { 0x04b0, KEY_BLUE }, + { 0x003d, KEY_TEXT }, + { 0x0113, KEY_SLEEP }, + { 0x0010, KEY_MUTE }, + { 0x0105, KEY_ESC }, + { 0x0009, KEY_SCREEN }, + { 0x010f, KEY_MENU }, + { 0x003f, KEY_CHANNEL }, + { 0x0013, KEY_REWIND }, + { 0x0012, KEY_PLAY }, + { 0x0011, KEY_FASTFORWARD }, + { 0x0005, KEY_PREVIOUS }, + { 0x0029, KEY_STOP }, + { 0x002b, KEY_NEXT }, + { 0x0041, KEY_EPG }, + { 0x0019, KEY_VIDEO }, + { 0x0016, KEY_AUDIO }, + { 0x0037, KEY_DOT }, + { 0x002a, KEY_AGAIN }, + { 0x002c, KEY_CAMERA }, + { 0x003c, KEY_NEW }, + { 0x0115, KEY_RECORD }, + { 0x010b, KEY_TIME }, + { 0x0043, KEY_VOLUMEUP }, + { 0x0042, KEY_VOLUMEDOWN }, + { 0x004b, KEY_CHANNELUP }, + { 0x004e, KEY_CHANNELDOWN }, }; static u8 af9015_ir_table_leadtek[] = { - 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, - 0x03, 0xfc, 0x56, 0xa9, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x4b, 0xb4, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, - 0x03, 0xfc, 0x4d, 0xb2, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x4e, 0xb1, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, - 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, - 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, - 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, - 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, - 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, - 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, - 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, - 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, - 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, - 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, - 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, - 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, - 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, - 0x03, 0xfc, 0x43, 0xbc, 0x00, 0x00, 0x00, - 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, - 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, - 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, - 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, - 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, - 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, - 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, - 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, - 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, - 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, - 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, - 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, - 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, - 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, - 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, - 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, - 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, - 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, - 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, - 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, - 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, - 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, - 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, - 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, - 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, - 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, - 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, - 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, - 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, + 0x03, 0xfc, 0x00, 0xff, 0x1a, 0x01, 0x00, /* KEY_POWER2 */ + 0x03, 0xfc, 0x56, 0xa9, 0xb4, 0x04, 0x00, /* KEY_TV */ + 0x03, 0xfc, 0x4b, 0xb4, 0xb3, 0x04, 0x00, /* KEY_RED */ + 0x03, 0xfc, 0x4c, 0xb3, 0xb2, 0x04, 0x00, /* KEY_GREEN */ + 0x03, 0xfc, 0x4d, 0xb2, 0xb1, 0x04, 0x00, /* KEY_YELLOW */ + 0x03, 0xfc, 0x4e, 0xb1, 0xb0, 0x04, 0x00, /* KEY_BLUE */ + 0x03, 0xfc, 0x1f, 0xe0, 0x3d, 0x00, 0x00, /* KEY_TEXT */ + 0x03, 0xfc, 0x40, 0xbf, 0x13, 0x01, 0x00, /* KEY_SLEEP */ + 0x03, 0xfc, 0x14, 0xeb, 0x10, 0x00, 0x00, /* KEY_MUTE */ + 0x03, 0xfc, 0x49, 0xb6, 0x05, 0x01, 0x00, /* KEY_ESC */ + 0x03, 0xfc, 0x50, 0xaf, 0x29, 0x00, 0x00, /* KEY_STOP (1)*/ + 0x03, 0xfc, 0x0c, 0xf3, 0x52, 0x00, 0x00, /* KEY_UP */ + 0x03, 0xfc, 0x03, 0xfc, 0x09, 0x00, 0x00, /* KEY_SCREEN */ + 0x03, 0xfc, 0x08, 0xf7, 0x50, 0x00, 0x00, /* KEY_LEFT */ + 0x03, 0xfc, 0x13, 0xec, 0x28, 0x00, 0x00, /* KEY_OK (1) */ + 0x03, 0xfc, 0x04, 0xfb, 0x4f, 0x00, 0x00, /* KEY_RIGHT */ + 0x03, 0xfc, 0x4f, 0xb0, 0x0f, 0x01, 0x00, /* KEY_MENU */ + 0x03, 0xfc, 0x10, 0xef, 0x51, 0x00, 0x00, /* KEY_DOWN */ + 0x03, 0xfc, 0x51, 0xae, 0x3f, 0x00, 0x00, /* KEY_CHANNEL */ + 0x03, 0xfc, 0x42, 0xbd, 0x13, 0x00, 0x00, /* KEY_REWIND */ + 0x03, 0xfc, 0x43, 0xbc, 0x12, 0x00, 0x00, /* KEY_PLAY */ + 0x03, 0xfc, 0x44, 0xbb, 0x11, 0x00, 0x00, /* KEY_FASTFORWARD */ + 0x03, 0xfc, 0x52, 0xad, 0x19, 0x00, 0x00, /* KEY_VIDEO (1) */ + 0x03, 0xfc, 0x54, 0xab, 0x05, 0x00, 0x00, /* KEY_PREVIOUS */ + 0x03, 0xfc, 0x46, 0xb9, 0x29, 0x00, 0x00, /* KEY_STOP (2) */ + 0x03, 0xfc, 0x55, 0xaa, 0x2b, 0x00, 0x00, /* KEY_NEXT */ + 0x03, 0xfc, 0x53, 0xac, 0x41, 0x00, 0x00, /* KEY_EPG */ + 0x03, 0xfc, 0x05, 0xfa, 0x1e, 0x00, 0x00, /* KEY_1 */ + 0x03, 0xfc, 0x06, 0xf9, 0x1f, 0x00, 0x00, /* KEY_2 */ + 0x03, 0xfc, 0x07, 0xf8, 0x20, 0x00, 0x00, /* KEY_3 */ + 0x03, 0xfc, 0x1e, 0xe1, 0x19, 0x00, 0x00, /* KEY_VIDEO (2) */ + 0x03, 0xfc, 0x09, 0xf6, 0x21, 0x00, 0x00, /* KEY_4 */ + 0x03, 0xfc, 0x0a, 0xf5, 0x22, 0x00, 0x00, /* KEY_5 */ + 0x03, 0xfc, 0x0b, 0xf4, 0x23, 0x00, 0x00, /* KEY_6 */ + 0x03, 0xfc, 0x1b, 0xe4, 0x16, 0x00, 0x00, /* KEY_AUDIO */ + 0x03, 0xfc, 0x0d, 0xf2, 0x24, 0x00, 0x00, /* KEY_7 */ + 0x03, 0xfc, 0x0e, 0xf1, 0x25, 0x00, 0x00, /* KEY_8 */ + 0x03, 0xfc, 0x0f, 0xf0, 0x26, 0x00, 0x00, /* KEY_9 */ + 0x03, 0xfc, 0x16, 0xe9, 0x28, 0x00, 0x00, /* KEY_OK (2) */ + 0x03, 0xfc, 0x41, 0xbe, 0x37, 0x00, 0x00, /* KEY_DOT */ + 0x03, 0xfc, 0x12, 0xed, 0x27, 0x00, 0x00, /* KEY_0 */ + 0x03, 0xfc, 0x11, 0xee, 0x2a, 0x00, 0x00, /* KEY_AGAIN */ + 0x03, 0xfc, 0x48, 0xb7, 0x2c, 0x00, 0x00, /* KEY_CAMERA */ + 0x03, 0xfc, 0x4a, 0xb5, 0x3c, 0x00, 0x00, /* KEY_NEW */ + 0x03, 0xfc, 0x47, 0xb8, 0x15, 0x01, 0x00, /* KEY_RECORD */ + 0x03, 0xfc, 0x45, 0xba, 0x0b, 0x01, 0x00, /* KEY_TIME */ + 0x03, 0xfc, 0x5e, 0xa1, 0x43, 0x00, 0x00, /* KEY_VOLUMEUP */ + 0x03, 0xfc, 0x5a, 0xa5, 0x42, 0x00, 0x00, /* KEY_VOLUMEDOWN */ + 0x03, 0xfc, 0x5b, 0xa4, 0x4b, 0x00, 0x00, /* KEY_CHANNELUP */ + 0x03, 0xfc, 0x5f, 0xa0, 0x4e, 0x00, 0x00, /* KEY_CHANNELDOWN */ }; /* TwinHan AzureWave AD-TU700(704J) */ @@ -746,4 +780,75 @@ static u8 af9015_ir_table_trekstor[] = { 0x00, 0xff, 0x84, 0x7b, 0x27, 0x07, 0x00, }; +/* MSI DIGIVOX mini III */ +static struct dvb_usb_rc_key af9015_rc_keys_msi_digivox_iii[] = { + { 0x0713, KEY_POWER }, /* [red power button] */ + { 0x073b, KEY_VIDEO }, /* Source */ + { 0x073e, KEY_ZOOM }, /* Zoom */ + { 0x070b, KEY_POWER2 }, /* ShutDown */ + { 0x071e, KEY_1 }, + { 0x071f, KEY_2 }, + { 0x0720, KEY_3 }, + { 0x0721, KEY_4 }, + { 0x0722, KEY_5 }, + { 0x0723, KEY_6 }, + { 0x0724, KEY_7 }, + { 0x0725, KEY_8 }, + { 0x0726, KEY_9 }, + { 0x0727, KEY_0 }, + { 0x0752, KEY_CHANNELUP }, /* CH+ */ + { 0x0751, KEY_CHANNELDOWN }, /* CH- */ + { 0x0750, KEY_VOLUMEUP }, /* Vol+ */ + { 0x074f, KEY_VOLUMEDOWN }, /* Vol- */ + { 0x0705, KEY_ESC }, /* [back up arrow] */ + { 0x0708, KEY_OK }, /* [enter arrow] */ + { 0x073f, KEY_RECORD }, /* Rec */ + { 0x0716, KEY_STOP }, /* Stop */ + { 0x072a, KEY_PLAY }, /* Play */ + { 0x073c, KEY_MUTE }, /* Mute */ + { 0x0718, KEY_UP }, + { 0x0707, KEY_DOWN }, + { 0x070f, KEY_LEFT }, + { 0x0715, KEY_RIGHT }, + { 0x0736, KEY_RED }, + { 0x0737, KEY_GREEN }, + { 0x072d, KEY_YELLOW }, + { 0x072e, KEY_BLUE }, +}; + +static u8 af9015_ir_table_msi_digivox_iii[] = { + 0x61, 0xd6, 0x43, 0xbc, 0x13, 0x07, 0x00, /* KEY_POWER */ + 0x61, 0xd6, 0x01, 0xfe, 0x3b, 0x07, 0x00, /* KEY_VIDEO */ + 0x61, 0xd6, 0x0b, 0xf4, 0x3e, 0x07, 0x00, /* KEY_ZOOM */ + 0x61, 0xd6, 0x03, 0xfc, 0x0b, 0x07, 0x00, /* KEY_POWER2 */ + 0x61, 0xd6, 0x04, 0xfb, 0x1e, 0x07, 0x00, /* KEY_1 */ + 0x61, 0xd6, 0x08, 0xf7, 0x1f, 0x07, 0x00, /* KEY_2 */ + 0x61, 0xd6, 0x02, 0xfd, 0x20, 0x07, 0x00, /* KEY_3 */ + 0x61, 0xd6, 0x0f, 0xf0, 0x21, 0x07, 0x00, /* KEY_4 */ + 0x61, 0xd6, 0x05, 0xfa, 0x22, 0x07, 0x00, /* KEY_5 */ + 0x61, 0xd6, 0x06, 0xf9, 0x23, 0x07, 0x00, /* KEY_6 */ + 0x61, 0xd6, 0x0c, 0xf3, 0x24, 0x07, 0x00, /* KEY_7 */ + 0x61, 0xd6, 0x0d, 0xf2, 0x25, 0x07, 0x00, /* KEY_8 */ + 0x61, 0xd6, 0x0a, 0xf5, 0x26, 0x07, 0x00, /* KEY_9 */ + 0x61, 0xd6, 0x11, 0xee, 0x27, 0x07, 0x00, /* KEY_0 */ + 0x61, 0xd6, 0x09, 0xf6, 0x52, 0x07, 0x00, /* KEY_CHANNELUP */ + 0x61, 0xd6, 0x07, 0xf8, 0x51, 0x07, 0x00, /* KEY_CHANNELDOWN */ + 0x61, 0xd6, 0x0e, 0xf1, 0x50, 0x07, 0x00, /* KEY_VOLUMEUP */ + 0x61, 0xd6, 0x13, 0xec, 0x4f, 0x07, 0x00, /* KEY_VOLUMEDOWN */ + 0x61, 0xd6, 0x10, 0xef, 0x05, 0x07, 0x00, /* KEY_ESC */ + 0x61, 0xd6, 0x12, 0xed, 0x08, 0x07, 0x00, /* KEY_OK */ + 0x61, 0xd6, 0x14, 0xeb, 0x3f, 0x07, 0x00, /* KEY_RECORD */ + 0x61, 0xd6, 0x15, 0xea, 0x16, 0x07, 0x00, /* KEY_STOP */ + 0x61, 0xd6, 0x16, 0xe9, 0x2a, 0x07, 0x00, /* KEY_PLAY */ + 0x61, 0xd6, 0x17, 0xe8, 0x3c, 0x07, 0x00, /* KEY_MUTE */ + 0x61, 0xd6, 0x18, 0xe7, 0x18, 0x07, 0x00, /* KEY_UP */ + 0x61, 0xd6, 0x19, 0xe6, 0x07, 0x07, 0x00, /* KEY_DOWN */ + 0x61, 0xd6, 0x1a, 0xe5, 0x0f, 0x07, 0x00, /* KEY_LEFT */ + 0x61, 0xd6, 0x1b, 0xe4, 0x15, 0x07, 0x00, /* KEY_RIGHT */ + 0x61, 0xd6, 0x1c, 0xe3, 0x36, 0x07, 0x00, /* KEY_RED */ + 0x61, 0xd6, 0x1d, 0xe2, 0x37, 0x07, 0x00, /* KEY_GREEN */ + 0x61, 0xd6, 0x1e, 0xe1, 0x2d, 0x07, 0x00, /* KEY_YELLOW */ + 0x61, 0xd6, 0x1f, 0xe0, 0x2e, 0x07, 0x00, /* KEY_BLUE */ +}; + #endif diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c index 2a53dd096eef8..05fb28e9c69e1 100644 --- a/drivers/media/dvb/dvb-usb/cxusb.c +++ b/drivers/media/dvb/dvb-usb/cxusb.c @@ -36,9 +36,11 @@ #include "tuner-xc2028.h" #include "tuner-simple.h" #include "mxl5005s.h" +#include "max2165.h" #include "dib7000p.h" #include "dib0070.h" #include "lgs8gxx.h" +#include "atbm8830.h" /* debug */ static int dvb_usb_cxusb_debug; @@ -714,6 +716,11 @@ static struct mxl5005s_config d680_dmb_tuner = { .AgcMasterByte = 0x00, }; +static struct max2165_config mygica_d689_max2165_cfg = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + /* Callbacks for DVB USB */ static int cxusb_fmd1216me_tuner_attach(struct dvb_usb_adapter *adap) { @@ -813,6 +820,14 @@ static int cxusb_d680_dmb_tuner_attach(struct dvb_usb_adapter *adap) return (fe == NULL) ? -EIO : 0; } +static int cxusb_mygica_d689_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_frontend *fe; + fe = dvb_attach(max2165_attach, adap->fe, + &adap->dev->i2c_adap, &mygica_d689_max2165_cfg); + return (fe == NULL) ? -EIO : 0; +} + static int cxusb_cx22702_frontend_attach(struct dvb_usb_adapter *adap) { u8 b; @@ -1160,6 +1175,55 @@ static int cxusb_d680_dmb_frontend_attach(struct dvb_usb_adapter *adap) return 0; } +static struct atbm8830_config mygica_d689_atbm8830_cfg = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x40, + .serial_ts = 0, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, +}; + +static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + + /* Select required USB configuration */ + if (usb_set_interface(d->udev, 0, 0) < 0) + err("set interface failed"); + + /* Unblock all USB pipes */ + usb_clear_halt(d->udev, + usb_sndbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.generic_bulk_ctrl_endpoint)); + usb_clear_halt(d->udev, + usb_rcvbulkpipe(d->udev, d->props.adapter[0].stream.endpoint)); + + + /* Reset the tuner */ + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 0) < 0) { + err("clear tuner gpio failed"); + return -EIO; + } + msleep(100); + if (cxusb_d680_dmb_gpio_tuner(d, 0x07, 1) < 0) { + err("set tuner gpio failed"); + return -EIO; + } + msleep(100); + + /* Attach frontend */ + adap->fe = dvb_attach(atbm8830_attach, &mygica_d689_atbm8830_cfg, + &d->i2c_adap); + if (adap->fe == NULL) + return -EIO; + + return 0; +} + /* * DViCO has shipped two devices with the same USB ID, but only one of them * needs a firmware download. Check the device class details to see if they @@ -1240,6 +1304,7 @@ static struct dvb_usb_device_properties cxusb_bluebird_nano2_properties; static struct dvb_usb_device_properties cxusb_bluebird_nano2_needsfirmware_properties; static struct dvb_usb_device_properties cxusb_aver_a868r_properties; static struct dvb_usb_device_properties cxusb_d680_dmb_properties; +static struct dvb_usb_device_properties cxusb_mygica_d689_properties; static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1268,6 +1333,8 @@ static int cxusb_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_d680_dmb_properties, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_mygica_d689_properties, + THIS_MODULE, NULL, adapter_nr) || 0) return 0; @@ -1294,6 +1361,7 @@ static struct usb_device_id cxusb_table [] = { { USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_A868R) }, { USB_DEVICE(USB_VID_DVICO, USB_PID_DVICO_BLUEBIRD_DUAL_4_REV_2) }, { USB_DEVICE(USB_VID_CONEXANT, USB_PID_CONEXANT_D680_DMB) }, + { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_D689) }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -1837,6 +1905,55 @@ static struct dvb_usb_device_properties cxusb_d680_dmb_properties = { } }; +static struct dvb_usb_device_properties cxusb_mygica_d689_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = CYPRESS_FX2, + + .size_of_priv = sizeof(struct cxusb_state), + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = cxusb_d680_dmb_streaming_ctrl, + .frontend_attach = cxusb_mygica_d689_frontend_attach, + .tuner_attach = cxusb_mygica_d689_tuner_attach, + + /* parameter for the MPEG2-data transfer */ + .stream = { + .type = USB_BULK, + .count = 5, + .endpoint = 0x02, + .u = { + .bulk = { + .buffersize = 8192, + } + } + }, + }, + }, + + .power_ctrl = cxusb_d680_dmb_power_ctrl, + + .i2c_algo = &cxusb_i2c_algo, + + .generic_bulk_ctrl_endpoint = 0x01, + + .rc_interval = 100, + .rc_key_map = d680_dmb_rc_keys, + .rc_key_map_size = ARRAY_SIZE(d680_dmb_rc_keys), + .rc_query = cxusb_d680_dmb_rc_query, + + .num_device_descs = 1, + .devices = { + { + "Mygica D689 DMB-TH", + { NULL }, + { &cxusb_table[19], NULL }, + }, + } +}; + static struct usb_driver cxusb_driver = { .name = "dvb_usb_cxusb", .probe = cxusb_probe, diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index 6bd8951ea02b0..684146f98eb75 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -558,8 +558,7 @@ static int dib0700_rc_query_legacy(struct dvb_usb_device *d, u32 *event, struct dib0700_rc_response { u8 report_id; u8 data_state; - u8 system_msb; - u8 system_lsb; + u16 system; u8 data; u8 not_data; }; @@ -589,37 +588,50 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event, return 0; } - if (actlen != sizeof(buf)) { - /* We didn't get back the 6 byte message we expected */ - err("Unexpected RC response size [%d]", actlen); - return -1; - } - poll_reply.report_id = buf[0]; - poll_reply.data_state = buf[1]; - poll_reply.system_msb = buf[2]; - poll_reply.system_lsb = buf[3]; - poll_reply.data = buf[4]; - poll_reply.not_data = buf[5]; + switch (dvb_usb_dib0700_ir_proto) { + case 0: + poll_reply.report_id = 0; + poll_reply.data_state = 1; + poll_reply.system = buf[2]; + poll_reply.data = buf[4]; + poll_reply.not_data = buf[5]; + + /* NEC protocol sends repeat code as 0 0 0 FF */ + if ((poll_reply.system == 0x00) && (poll_reply.data == 0x00) + && (poll_reply.not_data == 0xff)) { + poll_reply.data_state = 2; + break; + } + break; + default: + if (actlen != sizeof(buf)) { + /* We didn't get back the 6 byte message we expected */ + err("Unexpected RC response size [%d]", actlen); + return -1; + } + + poll_reply.report_id = buf[0]; + poll_reply.data_state = buf[1]; + poll_reply.system = (buf[2] << 8) | buf[3]; + poll_reply.data = buf[4]; + poll_reply.not_data = buf[5]; - /* - info("rid=%02x ds=%02x sm=%02x sl=%02x d=%02x nd=%02x\n", - poll_reply.report_id, poll_reply.data_state, - poll_reply.system_msb, poll_reply.system_lsb, - poll_reply.data, poll_reply.not_data); - */ + break; + } if ((poll_reply.data + poll_reply.not_data) != 0xff) { /* Key failed integrity check */ - err("key failed integrity check: %02x %02x %02x %02x", - poll_reply.system_msb, poll_reply.system_lsb, + err("key failed integrity check: %04x %02x %02x", + poll_reply.system, poll_reply.data, poll_reply.not_data); return -1; } + /* Find the key in the map */ for (i = 0; i < d->props.rc_key_map_size; i++) { - if (rc5_custom(&keymap[i]) == poll_reply.system_lsb && + if (rc5_custom(&keymap[i]) == (poll_reply.system & 0xff) && rc5_data(&keymap[i]) == poll_reply.data) { *event = keymap[i].event; found = 1; @@ -628,8 +640,8 @@ static int dib0700_rc_query_v1_20(struct dvb_usb_device *d, u32 *event, } if (found == 0) { - err("Unknown remote controller key: %02x %02x %02x %02x", - poll_reply.system_msb, poll_reply.system_lsb, + err("Unknown remote controller key: %04x %02x %02x", + poll_reply.system, poll_reply.data, poll_reply.not_data); d->last_event = 0; return 0; @@ -874,6 +886,49 @@ static struct dvb_usb_rc_key dib0700_rc_keys[] = { { 0x1d37, KEY_RECORD }, { 0x1d3b, KEY_GOTO }, { 0x1d3d, KEY_POWER }, + + /* Key codes for the Pixelview SBTVD remote (proto NEC) */ + { 0x8613, KEY_MUTE }, + { 0x8612, KEY_POWER }, + { 0x8601, KEY_1 }, + { 0x8602, KEY_2 }, + { 0x8603, KEY_3 }, + { 0x8604, KEY_4 }, + { 0x8605, KEY_5 }, + { 0x8606, KEY_6 }, + { 0x8607, KEY_7 }, + { 0x8608, KEY_8 }, + { 0x8609, KEY_9 }, + { 0x8600, KEY_0 }, + { 0x860d, KEY_CHANNELUP }, + { 0x8619, KEY_CHANNELDOWN }, + { 0x8610, KEY_VOLUMEUP }, + { 0x860c, KEY_VOLUMEDOWN }, + + { 0x860a, KEY_CAMERA }, + { 0x860b, KEY_ZOOM }, + { 0x861b, KEY_BACKSPACE }, + { 0x8615, KEY_ENTER }, + + { 0x861d, KEY_UP }, + { 0x861e, KEY_DOWN }, + { 0x860e, KEY_LEFT }, + { 0x860f, KEY_RIGHT }, + + { 0x8618, KEY_RECORD }, + { 0x861a, KEY_STOP }, + + /* Key codes for the EvolutePC TVWay+ remote (proto NEC) */ + { 0x7a00, KEY_MENU }, + { 0x7a01, KEY_RECORD }, + { 0x7a02, KEY_PLAY }, + { 0x7a03, KEY_STOP }, + { 0x7a10, KEY_CHANNELUP }, + { 0x7a11, KEY_CHANNELDOWN }, + { 0x7a12, KEY_VOLUMEUP }, + { 0x7a13, KEY_VOLUMEDOWN }, + { 0x7a40, KEY_POWER }, + { 0x7a41, KEY_MUTE }, }; /* STK7700P: Hauppauge Nova-T Stick, AVerMedia Volar */ @@ -1133,6 +1188,7 @@ static struct dib0070_config dib7770p_dib0070_config = { .clock_khz = 12000, .clock_pad_drive = 0, .flip_chip = 1, + .charge_pump = 2, }; static int dib7070_set_param_override(struct dvb_frontend *fe, struct dvb_frontend_parameters *fep) @@ -1209,6 +1265,16 @@ static int dib7070p_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int stk70x0p_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +{ + return dib7000p_pid_filter(adapter->fe, index, pid, onoff); +} + +static int stk70x0p_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +{ + return dib7000p_pid_filter_ctrl(adapter->fe, onoff); +} + static struct dibx000_bandwidth_config dib7070_bw_config_12_mhz = { 60000, 15000, // internal, sampling 1, 20, 3, 1, 0, // pll_cfg: prediv, ratio, range, reset, bypass @@ -1500,6 +1566,15 @@ static int dib807x_tuner_attach(struct dvb_usb_adapter *adap) return 0; } +static int stk807x_pid_filter(struct dvb_usb_adapter *adapter, int index, u16 pid, int onoff) +{ + return dib8000_pid_filter(adapter->fe, index, pid, onoff); +} + +static int stk807x_pid_filter_ctrl(struct dvb_usb_adapter *adapter, int onoff) +{ + return dib8000_pid_filter_ctrl(adapter->fe, onoff); +} /* STK807x */ static int stk807x_frontend_attach(struct dvb_usb_adapter *adap) @@ -1861,6 +1936,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XPVR) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK807XP) }, { USB_DEVICE(USB_VID_PIXELVIEW, USB_PID_PIXELVIEW_SBTVD) }, + { USB_DEVICE(USB_VID_EVOLUTEPC, USB_PID_TVWAY_PLUS) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -1895,6 +1971,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700p_frontend_attach, .tuner_attach = stk7700p_tuner_attach, @@ -1976,11 +2056,19 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700d_frontend_attach, .tuner_attach = stk7700d_tuner_attach, DIB0700_DEFAULT_STREAMING_CONFIG(0x02), }, { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700d_frontend_attach, .tuner_attach = stk7700d_tuner_attach, @@ -2023,6 +2111,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700P2_frontend_attach, .tuner_attach = stk7700d_tuner_attach, @@ -2055,6 +2147,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070p_frontend_attach, .tuner_attach = dib7070p_tuner_attach, @@ -2122,6 +2218,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070p_frontend_attach, .tuner_attach = dib7070p_tuner_attach, @@ -2157,6 +2257,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070pd_frontend_attach0, .tuner_attach = dib7070p_tuner_attach, @@ -2164,6 +2268,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .size_of_priv = sizeof(struct dib0700_adapter_state), }, { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070pd_frontend_attach1, .tuner_attach = dib7070p_tuner_attach, @@ -2210,6 +2318,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7700ph_frontend_attach, .tuner_attach = stk7700ph_tuner_attach, @@ -2322,6 +2434,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk70x0p_pid_filter, + .pid_filter_ctrl = stk70x0p_pid_filter_ctrl, .frontend_attach = stk7070p_frontend_attach, .tuner_attach = dib7770p_tuner_attach, @@ -2353,6 +2469,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 1, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk807x_pid_filter, + .pid_filter_ctrl = stk807x_pid_filter_ctrl, .frontend_attach = stk807x_frontend_attach, .tuner_attach = dib807x_tuner_attach, @@ -2363,7 +2483,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 2, + .num_device_descs = 3, .devices = { { "DiBcom STK807xP reference design", { &dib0700_usb_id_table[62], NULL }, @@ -2373,6 +2493,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[63], NULL }, { NULL }, }, + { "EvolutePC TVWay+", + { &dib0700_usb_id_table[64], NULL }, + { NULL }, + }, }, .rc_interval = DEFAULT_RC_INTERVAL, @@ -2384,6 +2508,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { .num_adapters = 2, .adapter = { { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk807x_pid_filter, + .pid_filter_ctrl = stk807x_pid_filter_ctrl, .frontend_attach = stk807xpvr_frontend_attach0, .tuner_attach = dib807x_tuner_attach, @@ -2393,6 +2521,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { sizeof(struct dib0700_adapter_state), }, { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .pid_filter_count = 32, + .pid_filter = stk807x_pid_filter, + .pid_filter_ctrl = stk807x_pid_filter_ctrl, .frontend_attach = stk807xpvr_frontend_attach1, .tuner_attach = dib807x_tuner_attach, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c index 8a7d87bcd1d98..df1ec3e69f4ac 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c @@ -88,6 +88,7 @@ int dvb_usb_adapter_dvb_init(struct dvb_usb_adapter *adap, short *adapter_nums) goto err; } adap->dvb_adap.priv = adap; + adap->dvb_adap.fe_ioctl_override = adap->props.fe_ioctl_override; if (adap->dev->props.read_mac_address) { if (adap->dev->props.read_mac_address(adap->dev,adap->dvb_adap.proposed_mac) == 0) diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index a548c14c19444..f1602d4ace6db 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -27,6 +27,7 @@ #define USB_VID_DIBCOM 0x10b8 #define USB_VID_DPOSH 0x1498 #define USB_VID_DVICO 0x0fe9 +#define USB_VID_E3C 0x18b4 #define USB_VID_ELGATO 0x0fd9 #define USB_VID_EMPIA 0xeb1a #define USB_VID_GENPIX 0x09c0 @@ -61,6 +62,7 @@ #define USB_VID_XTENSIONS 0x1ae7 #define USB_VID_HUMAX_COEX 0x10b9 #define USB_VID_774 0x7a69 +#define USB_VID_EVOLUTEPC 0x1e59 /* Product IDs */ #define USB_PID_ADSTECH_USB2_COLD 0xa333 @@ -103,6 +105,11 @@ #define USB_PID_DIBCOM_STK7770P 0x1e80 #define USB_PID_DPOSH_M9206_COLD 0x9206 #define USB_PID_DPOSH_M9206_WARM 0xa090 +#define USB_PID_E3C_EC168 0x1689 +#define USB_PID_E3C_EC168_2 0xfffa +#define USB_PID_E3C_EC168_3 0xfffb +#define USB_PID_E3C_EC168_4 0x1001 +#define USB_PID_E3C_EC168_5 0x1002 #define USB_PID_UNIWILL_STK7700P 0x6003 #define USB_PID_GENIUS_TVGO_DVB_T03 0x4012 #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0 @@ -115,6 +122,7 @@ #define USB_PID_KWORLD_395U_3 0xe395 #define USB_PID_KWORLD_MC810 0xc810 #define USB_PID_KWORLD_PC160_2T 0xc160 +#define USB_PID_KWORLD_PC160_T 0xc161 #define USB_PID_KWORLD_VSTREAM_COLD 0x17de #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 @@ -271,10 +279,13 @@ #define USB_PID_TELESTAR_STARSTICK_2 0x8000 #define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807 #define USB_PID_SONY_PLAYTV 0x0003 +#define USB_PID_MYGICA_D689 0xd811 #define USB_PID_ELGATO_EYETV_DTT 0x0021 #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_WARM 0x5001 #define USB_PID_FRIIO_WHITE 0x0001 +#define USB_PID_TVWAY_PLUS 0x0002 +#define USB_PID_SVEON_STV20 0xe39d #endif diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index fe2b87efb3f1a..0143aef19ecdd 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -162,6 +162,9 @@ struct dvb_usb_adapter_properties { struct usb_data_stream_properties stream; int size_of_priv; + + int (*fe_ioctl_override) (struct dvb_frontend *, + unsigned int, void *, unsigned int); }; /** diff --git a/drivers/media/dvb/dvb-usb/ec168.c b/drivers/media/dvb/dvb-usb/ec168.c new file mode 100644 index 0000000000000..52f5d4f0f230f --- /dev/null +++ b/drivers/media/dvb/dvb-usb/ec168.c @@ -0,0 +1,440 @@ +/* + * E3C EC168 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "ec168.h" +#include "ec100.h" +#include "mxl5005s.h" + +/* debug */ +static int dvb_usb_ec168_debug; +module_param_named(debug, dvb_usb_ec168_debug, int, 0644); +MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS); +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +static struct ec100_config ec168_ec100_config; + +static int ec168_rw_udev(struct usb_device *udev, struct ec168_req *req) +{ + int ret; + unsigned int pipe; + u8 request, requesttype; + u8 buf[req->size]; + + switch (req->cmd) { + case DOWNLOAD_FIRMWARE: + case GPIO: + case WRITE_I2C: + case STREAMING_CTRL: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = req->cmd; + break; + case READ_I2C: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = req->cmd; + break; + case GET_CONFIG: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = CONFIG; + break; + case SET_CONFIG: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = CONFIG; + break; + case WRITE_DEMOD: + requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT); + request = DEMOD_RW; + break; + case READ_DEMOD: + requesttype = (USB_TYPE_VENDOR | USB_DIR_IN); + request = DEMOD_RW; + break; + default: + err("unknown command:%02x", req->cmd); + ret = -EPERM; + goto error; + } + + if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) { + /* write */ + memcpy(buf, req->data, req->size); + pipe = usb_sndctrlpipe(udev, 0); + } else { + /* read */ + pipe = usb_rcvctrlpipe(udev, 0); + } + + msleep(1); /* avoid I2C errors */ + + ret = usb_control_msg(udev, pipe, request, requesttype, req->value, + req->index, buf, sizeof(buf), EC168_USB_TIMEOUT); + + ec168_debug_dump(request, requesttype, req->value, req->index, buf, + req->size, deb_xfer); + + if (ret < 0) + goto error; + else + ret = 0; + + /* read request, copy returned data to return buf */ + if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN)) + memcpy(req->data, buf, req->size); + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec168_ctrl_msg(struct dvb_usb_device *d, struct ec168_req *req) +{ + return ec168_rw_udev(d->udev, req); +} + +/* I2C */ +static int ec168_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], + int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct ec168_req req; + int i = 0; + int ret; + + if (num > 2) + return -EINVAL; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + while (i < num) { + if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) { + if (msg[i].addr == ec168_ec100_config.demod_address) { + req.cmd = READ_DEMOD; + req.value = 0; + req.index = 0xff00 + msg[i].buf[0]; /* reg */ + req.size = msg[i+1].len; /* bytes to read */ + req.data = &msg[i+1].buf[0]; + ret = ec168_ctrl_msg(d, &req); + i += 2; + } else { + err("I2C read not implemented"); + ret = -ENOSYS; + i += 2; + } + } else { + if (msg[i].addr == ec168_ec100_config.demod_address) { + req.cmd = WRITE_DEMOD; + req.value = msg[i].buf[1]; /* val */ + req.index = 0xff00 + msg[i].buf[0]; /* reg */ + req.size = 0; + req.data = NULL; + ret = ec168_ctrl_msg(d, &req); + i += 1; + } else { + req.cmd = WRITE_I2C; + req.value = msg[i].buf[0]; /* val */ + req.index = 0x0100 + msg[i].addr; /* I2C addr */ + req.size = msg[i].len-1; + req.data = &msg[i].buf[1]; + ret = ec168_ctrl_msg(d, &req); + i += 1; + } + } + if (ret) + goto error; + + } + ret = i; + +error: + mutex_unlock(&d->i2c_mutex); + return i; +} + + +static u32 ec168_i2c_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm ec168_i2c_algo = { + .master_xfer = ec168_i2c_xfer, + .functionality = ec168_i2c_func, +}; + +/* Callbacks for DVB USB */ +static struct ec100_config ec168_ec100_config = { + .demod_address = 0xff, /* not real address, demod is integrated */ +}; + +static int ec168_ec100_frontend_attach(struct dvb_usb_adapter *adap) +{ + deb_info("%s:\n", __func__); + adap->fe = dvb_attach(ec100_attach, &ec168_ec100_config, + &adap->dev->i2c_adap); + if (adap->fe == NULL) + return -ENODEV; + + return 0; +} + +static struct mxl5005s_config ec168_mxl5003s_config = { + .i2c_address = 0xc6, + .if_freq = IF_FREQ_4570000HZ, + .xtal_freq = CRYSTAL_FREQ_16000000HZ, + .agc_mode = MXL_SINGLE_AGC, + .tracking_filter = MXL_TF_OFF, + .rssi_enable = MXL_RSSI_ENABLE, + .cap_select = MXL_CAP_SEL_ENABLE, + .div_out = MXL_DIV_OUT_4, + .clock_out = MXL_CLOCK_OUT_DISABLE, + .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM, + .top = MXL5005S_TOP_25P2, + .mod_mode = MXL_DIGITAL_MODE, + .if_mode = MXL_ZERO_IF, + .AgcMasterByte = 0x00, +}; + +static int ec168_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap) +{ + deb_info("%s:\n", __func__); + return dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap, + &ec168_mxl5003s_config) == NULL ? -ENODEV : 0; +} + +static int ec168_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct ec168_req req = {STREAMING_CTRL, 0x7f01, 0x0202, 0, NULL}; + deb_info("%s: onoff:%d\n", __func__, onoff); + if (onoff) + req.index = 0x0102; + return ec168_ctrl_msg(adap->dev, &req); +} + +static int ec168_download_firmware(struct usb_device *udev, + const struct firmware *fw) +{ + int i, len, packets, remainder, ret; + u16 addr = 0x0000; /* firmware start address */ + struct ec168_req req = {DOWNLOAD_FIRMWARE, 0, 0, 0, NULL}; + deb_info("%s:\n", __func__); + + #define FW_PACKET_MAX_DATA 2048 + packets = fw->size / FW_PACKET_MAX_DATA; + remainder = fw->size % FW_PACKET_MAX_DATA; + len = FW_PACKET_MAX_DATA; + for (i = 0; i <= packets; i++) { + if (i == packets) /* set size of the last packet */ + len = remainder; + + req.size = len; + req.data = (u8 *)(fw->data + i * FW_PACKET_MAX_DATA); + req.index = addr; + addr += FW_PACKET_MAX_DATA; + + ret = ec168_rw_udev(udev, &req); + if (ret) { + err("firmware download failed:%d packet:%d", ret, i); + goto error; + } + } + req.size = 0; + + /* set "warm"? */ + req.cmd = SET_CONFIG; + req.value = 0; + req.index = 0x0001; + ret = ec168_rw_udev(udev, &req); + if (ret) + goto error; + + /* really needed - no idea what does */ + req.cmd = GPIO; + req.value = 0; + req.index = 0x0206; + ret = ec168_rw_udev(udev, &req); + if (ret) + goto error; + + /* activate tuner I2C? */ + req.cmd = WRITE_I2C; + req.value = 0; + req.index = 0x00c6; + ret = ec168_rw_udev(udev, &req); + if (ret) + goto error; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec168_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, int *cold) +{ + int ret; + u8 reply; + struct ec168_req req = {GET_CONFIG, 0, 1, sizeof(reply), &reply}; + deb_info("%s:\n", __func__); + + ret = ec168_rw_udev(udev, &req); + if (ret) + goto error; + + deb_info("%s: reply:%02x\n", __func__, reply); + + if (reply == 0x01) + *cold = 0; + else + *cold = 1; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +/* DVB USB Driver stuff */ +static struct dvb_usb_device_properties ec168_properties; + +static int ec168_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret; + deb_info("%s: interface:%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + ret = dvb_usb_device_init(intf, &ec168_properties, THIS_MODULE, NULL, + adapter_nr); + if (ret) + goto error; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +#define E3C_EC168_1689 0 +#define E3C_EC168_FFFA 1 +#define E3C_EC168_FFFB 2 +#define E3C_EC168_1001 3 +#define E3C_EC168_1002 4 + +static struct usb_device_id ec168_id[] = { + [E3C_EC168_1689] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168)}, + [E3C_EC168_FFFA] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_2)}, + [E3C_EC168_FFFB] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_3)}, + [E3C_EC168_1001] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_4)}, + [E3C_EC168_1002] = + {USB_DEVICE(USB_VID_E3C, USB_PID_E3C_EC168_5)}, + {} /* terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, ec168_id); + +static struct dvb_usb_device_properties ec168_properties = { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = ec168_download_firmware, + .firmware = "dvb-usb-ec168.fw", + .no_reconnect = 1, + + .size_of_priv = 0, + + .num_adapters = 1, + .adapter = { + { + .streaming_ctrl = ec168_streaming_ctrl, + .frontend_attach = ec168_ec100_frontend_attach, + .tuner_attach = ec168_mxl5003s_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x82, + .u = { + .bulk = { + .buffersize = (32*512), + } + } + }, + } + }, + + .identify_state = ec168_identify_state, + + .i2c_algo = &ec168_i2c_algo, + + .num_device_descs = 1, + .devices = { + { + .name = "E3C EC168 DVB-T USB2.0 reference design", + .cold_ids = { + &ec168_id[E3C_EC168_1689], + &ec168_id[E3C_EC168_FFFA], + &ec168_id[E3C_EC168_FFFB], + &ec168_id[E3C_EC168_1001], + &ec168_id[E3C_EC168_1002], + NULL}, + .warm_ids = {NULL}, + }, + } +}; + +static struct usb_driver ec168_driver = { + .name = "dvb_usb_ec168", + .probe = ec168_probe, + .disconnect = dvb_usb_device_exit, + .id_table = ec168_id, +}; + +/* module stuff */ +static int __init ec168_module_init(void) +{ + int ret; + deb_info("%s:\n", __func__); + ret = usb_register(&ec168_driver); + if (ret) + err("module init failed:%d", ret); + + return ret; +} + +static void __exit ec168_module_exit(void) +{ + deb_info("%s:\n", __func__); + /* deregister this driver from the USB subsystem */ + usb_deregister(&ec168_driver); +} + +module_init(ec168_module_init); +module_exit(ec168_module_exit); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("E3C EC168 DVB-T USB2.0 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/ec168.h b/drivers/media/dvb/dvb-usb/ec168.h new file mode 100644 index 0000000000000..e7e0b831314ee --- /dev/null +++ b/drivers/media/dvb/dvb-usb/ec168.h @@ -0,0 +1,73 @@ +/* + * E3C EC168 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef EC168_H +#define EC168_H + +#define DVB_USB_LOG_PREFIX "ec168" +#include "dvb-usb.h" + +#define deb_info(args...) dprintk(dvb_usb_ec168_debug, 0x01, args) +#define deb_rc(args...) dprintk(dvb_usb_ec168_debug, 0x02, args) +#define deb_xfer(args...) dprintk(dvb_usb_ec168_debug, 0x04, args) +#define deb_reg(args...) dprintk(dvb_usb_ec168_debug, 0x08, args) +#define deb_i2c(args...) dprintk(dvb_usb_ec168_debug, 0x10, args) +#define deb_fw(args...) dprintk(dvb_usb_ec168_debug, 0x20, args) + +#define ec168_debug_dump(r, t, v, i, b, l, func) { \ + int loop_; \ + func("%02x %02x %02x %02x %02x %02x %02x %02x", \ + t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \ + if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \ + func(" >>> "); \ + else \ + func(" <<< "); \ + for (loop_ = 0; loop_ < l; loop_++) \ + func("%02x ", b[loop_]); \ + func("\n");\ +} + +#define EC168_USB_TIMEOUT 1000 + +struct ec168_req { + u8 cmd; /* [1] */ + u16 value; /* [2|3] */ + u16 index; /* [4|5] */ + u16 size; /* [6|7] */ + u8 *data; +}; + +enum ec168_cmd { + DOWNLOAD_FIRMWARE = 0x00, + CONFIG = 0x01, + DEMOD_RW = 0x03, + GPIO = 0x04, + STREAMING_CTRL = 0x10, + READ_I2C = 0x20, + WRITE_I2C = 0x21, + HID_DOWNLOAD = 0x30, + GET_CONFIG, + SET_CONFIG, + READ_DEMOD, + WRITE_DEMOD, +}; + +#endif diff --git a/drivers/media/dvb/dvb-usb/friio-fe.c b/drivers/media/dvb/dvb-usb/friio-fe.c index c4dfe25cf60dc..9cbbe42ca44be 100644 --- a/drivers/media/dvb/dvb-usb/friio-fe.c +++ b/drivers/media/dvb/dvb-usb/friio-fe.c @@ -232,12 +232,6 @@ static int jdvbt90502_read_status(struct dvb_frontend *fe, fe_status_t *state) return 0; } -static int jdvbt90502_read_ber(struct dvb_frontend *fe, u32 *ber) -{ - *ber = 0; - return 0; -} - static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { @@ -264,26 +258,26 @@ static int jdvbt90502_read_signal_strength(struct dvb_frontend *fe, return 0; } -static int jdvbt90502_read_snr(struct dvb_frontend *fe, u16 *snr) -{ - *snr = 0x0101; - return 0; -} - -static int jdvbt90502_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) -{ - *ucblocks = 0; - return 0; -} -static int jdvbt90502_get_tune_settings(struct dvb_frontend *fe, - struct dvb_frontend_tune_settings *fs) +/* filter out un-supported properties to notify users */ +static int jdvbt90502_set_property(struct dvb_frontend *fe, + struct dtv_property *tvp) { - fs->min_delay_ms = 500; - fs->step_size = 0; - fs->max_drift = 0; - - return 0; + int r = 0; + + switch (tvp->cmd) { + case DTV_DELIVERY_SYSTEM: + if (tvp->u.data != SYS_ISDBT) + r = -EINVAL; + break; + case DTV_CLEAR: + case DTV_TUNE: + case DTV_FREQUENCY: + break; + default: + r = -EINVAL; + } + return r; } static int jdvbt90502_get_frontend(struct dvb_frontend *fe, @@ -314,6 +308,9 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe, deb_fe("%s: Freq:%d\n", __func__, p->frequency); + /* for recovery from DTV_CLEAN */ + fe->dtv_property_cache.delivery_system = SYS_ISDBT; + ret = jdvbt90502_pll_set_freq(state, p->frequency); if (ret) { deb_fe("%s:ret == %d\n", __func__, ret); @@ -323,12 +320,6 @@ static int jdvbt90502_set_frontend(struct dvb_frontend *fe, return 0; } -static int jdvbt90502_sleep(struct dvb_frontend *fe) -{ - deb_fe("%s called.\n", __func__); - return 0; -} - /** * (reg, val) commad list to initialize this module. @@ -394,6 +385,7 @@ static int jdvbt90502_init(struct dvb_frontend *fe) if (ret != 1) goto error; } + fe->dtv_property_cache.delivery_system = SYS_ISDBT; msleep(100); return 0; @@ -468,16 +460,13 @@ static struct dvb_frontend_ops jdvbt90502_ops = { .release = jdvbt90502_release, .init = jdvbt90502_init, - .sleep = jdvbt90502_sleep, .write = _jdvbt90502_write, + .set_property = jdvbt90502_set_property, + .set_frontend = jdvbt90502_set_frontend, .get_frontend = jdvbt90502_get_frontend, - .get_tune_settings = jdvbt90502_get_tune_settings, .read_status = jdvbt90502_read_status, - .read_ber = jdvbt90502_read_ber, .read_signal_strength = jdvbt90502_read_signal_strength, - .read_snr = jdvbt90502_read_snr, - .read_ucblocks = jdvbt90502_read_ucblocks, }; diff --git a/drivers/media/dvb/firewire/Kconfig b/drivers/media/dvb/firewire/Kconfig index 69028253e984f..4afa29256df11 100644 --- a/drivers/media/dvb/firewire/Kconfig +++ b/drivers/media/dvb/firewire/Kconfig @@ -1,6 +1,6 @@ config DVB_FIREDTV tristate "FireDTV and FloppyDTV" - depends on DVB_CORE && IEEE1394 + depends on DVB_CORE && (FIREWIRE || IEEE1394) help Support for DVB receivers from Digital Everywhere which are connected via IEEE 1394 (FireWire). @@ -13,8 +13,11 @@ config DVB_FIREDTV if DVB_FIREDTV +config DVB_FIREDTV_FIREWIRE + def_bool FIREWIRE = y || (FIREWIRE = m && DVB_FIREDTV = m) + config DVB_FIREDTV_IEEE1394 - def_bool IEEE1394 + def_bool IEEE1394 = y || (IEEE1394 = m && DVB_FIREDTV = m) config DVB_FIREDTV_INPUT def_bool INPUT = y || (INPUT = m && DVB_FIREDTV = m) diff --git a/drivers/media/dvb/firewire/Makefile b/drivers/media/dvb/firewire/Makefile index 2034695ba1942..da84203d51c62 100644 --- a/drivers/media/dvb/firewire/Makefile +++ b/drivers/media/dvb/firewire/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_DVB_FIREDTV) += firedtv.o firedtv-y := firedtv-avc.o firedtv-ci.o firedtv-dvb.o firedtv-fe.o +firedtv-$(CONFIG_DVB_FIREDTV_FIREWIRE) += firedtv-fw.o firedtv-$(CONFIG_DVB_FIREDTV_IEEE1394) += firedtv-1394.o firedtv-$(CONFIG_DVB_FIREDTV_INPUT) += firedtv-rc.o diff --git a/drivers/media/dvb/firewire/firedtv-1394.c b/drivers/media/dvb/firewire/firedtv-1394.c index 2b6eeeab5b257..7c5459c27b753 100644 --- a/drivers/media/dvb/firewire/firedtv-1394.c +++ b/drivers/media/dvb/firewire/firedtv-1394.c @@ -1,5 +1,5 @@ /* - * FireDTV driver (formerly known as FireSAT) + * FireDTV driver -- ieee1394 I/O backend * * Copyright (C) 2004 Andreas Monitzer * Copyright (C) 2007-2008 Ben Backx @@ -26,13 +26,16 @@ #include #include +#include + #include "firedtv.h" static LIST_HEAD(node_list); static DEFINE_SPINLOCK(node_list_lock); -#define FIREWIRE_HEADER_SIZE 4 -#define CIP_HEADER_SIZE 8 +#define CIP_HEADER_SIZE 8 +#define MPEG2_TS_HEADER_SIZE 4 +#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188) static void rawiso_activity_cb(struct hpsb_iso *iso) { @@ -62,20 +65,20 @@ static void rawiso_activity_cb(struct hpsb_iso *iso) buf = dma_region_i(&iso->data_buf, unsigned char, iso->infos[packet].offset + CIP_HEADER_SIZE); count = (iso->infos[packet].len - CIP_HEADER_SIZE) / - (188 + FIREWIRE_HEADER_SIZE); + MPEG2_TS_SOURCE_PACKET_SIZE; /* ignore empty packet */ if (iso->infos[packet].len <= CIP_HEADER_SIZE) continue; while (count--) { - if (buf[FIREWIRE_HEADER_SIZE] == 0x47) + if (buf[MPEG2_TS_HEADER_SIZE] == 0x47) dvb_dmx_swfilter_packets(&fdtv->demux, - &buf[FIREWIRE_HEADER_SIZE], 1); + &buf[MPEG2_TS_HEADER_SIZE], 1); else dev_err(fdtv->device, "skipping invalid packet\n"); - buf += 188 + FIREWIRE_HEADER_SIZE; + buf += MPEG2_TS_SOURCE_PACKET_SIZE; } } out: @@ -87,15 +90,20 @@ static inline struct node_entry *node_of(struct firedtv *fdtv) return container_of(fdtv->device, struct unit_directory, device)->ne; } -static int node_lock(struct firedtv *fdtv, u64 addr, void *data, __be32 arg) +static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) { - return hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, data, - (__force quadlet_t)arg); + int ret; + + ret = hpsb_node_lock(node_of(fdtv), addr, EXTCODE_COMPARE_SWAP, + (__force quadlet_t *)&data[1], (__force quadlet_t)data[0]); + data[0] = data[1]; + + return ret; } -static int node_read(struct firedtv *fdtv, u64 addr, void *data, size_t len) +static int node_read(struct firedtv *fdtv, u64 addr, void *data) { - return hpsb_node_read(node_of(fdtv), addr, data, len); + return hpsb_node_read(node_of(fdtv), addr, data, 4); } static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) @@ -212,6 +220,7 @@ static int node_probe(struct device *dev) goto fail; avc_register_remote_control(fdtv); + return 0; fail: spin_lock_irq(&node_list_lock); @@ -220,6 +229,7 @@ static int node_probe(struct device *dev) fdtv_unregister_rc(fdtv); fail_free: kfree(fdtv); + return err; } @@ -233,10 +243,9 @@ static int node_remove(struct device *dev) list_del(&fdtv->list); spin_unlock_irq(&node_list_lock); - cancel_work_sync(&fdtv->remote_ctrl_work); fdtv_unregister_rc(fdtv); - kfree(fdtv); + return 0; } @@ -252,6 +261,7 @@ static int node_update(struct unit_directory *ud) static struct hpsb_protocol_driver fdtv_driver = { .name = "firedtv", + .id_table = fdtv_id_table, .update = node_update, .driver = { .probe = node_probe, @@ -264,12 +274,11 @@ static struct hpsb_highlevel fdtv_highlevel = { .fcp_request = fcp_request, }; -int __init fdtv_1394_init(struct ieee1394_device_id id_table[]) +int __init fdtv_1394_init(void) { int ret; hpsb_register_highlevel(&fdtv_highlevel); - fdtv_driver.id_table = id_table; ret = hpsb_register_protocol(&fdtv_driver); if (ret) { printk(KERN_ERR "firedtv: failed to register protocol\n"); diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c index 485d061319abb..50c42a4b972b1 100644 --- a/drivers/media/dvb/firewire/firedtv-avc.c +++ b/drivers/media/dvb/firewire/firedtv-avc.c @@ -1236,14 +1236,14 @@ int avc_ca_get_mmi(struct firedtv *fdtv, char *mmi_object, unsigned int *len) #define CMP_OUTPUT_PLUG_CONTROL_REG_0 0xfffff0000904ULL -static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) +static int cmp_read(struct firedtv *fdtv, u64 addr, __be32 *data) { int ret; if (mutex_lock_interruptible(&fdtv->avc_mutex)) return -EINTR; - ret = fdtv->backend->read(fdtv, addr, buf, len); + ret = fdtv->backend->read(fdtv, addr, data); if (ret < 0) dev_err(fdtv->device, "CMP: read I/O error\n"); @@ -1251,14 +1251,14 @@ static int cmp_read(struct firedtv *fdtv, void *buf, u64 addr, size_t len) return ret; } -static int cmp_lock(struct firedtv *fdtv, void *data, u64 addr, __be32 arg) +static int cmp_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) { int ret; if (mutex_lock_interruptible(&fdtv->avc_mutex)) return -EINTR; - ret = fdtv->backend->lock(fdtv, addr, data, arg); + ret = fdtv->backend->lock(fdtv, addr, data); if (ret < 0) dev_err(fdtv->device, "CMP: lock I/O error\n"); @@ -1288,25 +1288,25 @@ static inline void set_opcr(__be32 *opcr, u32 value, u32 mask, u32 shift) int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) { - __be32 old_opcr, opcr; + __be32 old_opcr, opcr[2]; u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); int attempts = 0; int ret; - ret = cmp_read(fdtv, &opcr, opcr_address, 4); + ret = cmp_read(fdtv, opcr_address, opcr); if (ret < 0) return ret; repeat: - if (!get_opcr_online(opcr)) { + if (!get_opcr_online(*opcr)) { dev_err(fdtv->device, "CMP: output offline\n"); return -EBUSY; } - old_opcr = opcr; + old_opcr = *opcr; - if (get_opcr_p2p_connections(opcr)) { - if (get_opcr_channel(opcr) != channel) { + if (get_opcr_p2p_connections(*opcr)) { + if (get_opcr_channel(*opcr) != channel) { dev_err(fdtv->device, "CMP: cannot change channel\n"); return -EBUSY; } @@ -1314,11 +1314,11 @@ int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) /* We don't allocate isochronous resources. */ } else { - set_opcr_channel(&opcr, channel); - set_opcr_data_rate(&opcr, 2); /* S400 */ + set_opcr_channel(opcr, channel); + set_opcr_data_rate(opcr, 2); /* S400 */ /* FIXME: this is for the worst case - optimize */ - set_opcr_overhead_id(&opcr, 0); + set_opcr_overhead_id(opcr, 0); /* * FIXME: allocate isochronous channel and bandwidth at IRM @@ -1326,13 +1326,16 @@ int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) */ } - set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) + 1); + set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) + 1); - ret = cmp_lock(fdtv, &opcr, opcr_address, old_opcr); + opcr[1] = *opcr; + opcr[0] = old_opcr; + + ret = cmp_lock(fdtv, opcr_address, opcr); if (ret < 0) return ret; - if (old_opcr != opcr) { + if (old_opcr != *opcr) { /* * FIXME: if old_opcr.P2P_Connections > 0, * deallocate isochronous channel and bandwidth at IRM @@ -1350,27 +1353,30 @@ int cmp_establish_pp_connection(struct firedtv *fdtv, int plug, int channel) void cmp_break_pp_connection(struct firedtv *fdtv, int plug, int channel) { - __be32 old_opcr, opcr; + __be32 old_opcr, opcr[2]; u64 opcr_address = CMP_OUTPUT_PLUG_CONTROL_REG_0 + (plug << 2); int attempts = 0; - if (cmp_read(fdtv, &opcr, opcr_address, 4) < 0) + if (cmp_read(fdtv, opcr_address, opcr) < 0) return; repeat: - if (!get_opcr_online(opcr) || !get_opcr_p2p_connections(opcr) || - get_opcr_channel(opcr) != channel) { + if (!get_opcr_online(*opcr) || !get_opcr_p2p_connections(*opcr) || + get_opcr_channel(*opcr) != channel) { dev_err(fdtv->device, "CMP: no connection to break\n"); return; } - old_opcr = opcr; - set_opcr_p2p_connections(&opcr, get_opcr_p2p_connections(opcr) - 1); + old_opcr = *opcr; + set_opcr_p2p_connections(opcr, get_opcr_p2p_connections(*opcr) - 1); + + opcr[1] = *opcr; + opcr[0] = old_opcr; - if (cmp_lock(fdtv, &opcr, opcr_address, old_opcr) < 0) + if (cmp_lock(fdtv, opcr_address, opcr) < 0) return; - if (old_opcr != opcr) { + if (old_opcr != *opcr) { /* * FIXME: if old_opcr.P2P_Connections == 1, i.e. we were last * owner, deallocate isochronous channel and bandwidth at IRM diff --git a/drivers/media/dvb/firewire/firedtv-dvb.c b/drivers/media/dvb/firewire/firedtv-dvb.c index 5742fde79d99d..fc9996c13e134 100644 --- a/drivers/media/dvb/firewire/firedtv-dvb.c +++ b/drivers/media/dvb/firewire/firedtv-dvb.c @@ -297,7 +297,7 @@ struct firedtv *fdtv_alloc(struct device *dev, #define AVC_UNIT_SPEC_ID_ENTRY 0x00a02d #define AVC_SW_VERSION_ENTRY 0x010001 -static struct ieee1394_device_id fdtv_id_table[] = { +const struct ieee1394_device_id fdtv_id_table[] = { { /* FloppyDTV S/CI and FloppyDTV S2 */ .match_flags = MATCH_FLAGS, @@ -346,12 +346,23 @@ MODULE_DEVICE_TABLE(ieee1394, fdtv_id_table); static int __init fdtv_init(void) { - return fdtv_1394_init(fdtv_id_table); + int ret; + + ret = fdtv_fw_init(); + if (ret < 0) + return ret; + + ret = fdtv_1394_init(); + if (ret < 0) + fdtv_fw_exit(); + + return ret; } static void __exit fdtv_exit(void) { fdtv_1394_exit(); + fdtv_fw_exit(); } module_init(fdtv_init); diff --git a/drivers/media/dvb/firewire/firedtv-fw.c b/drivers/media/dvb/firewire/firedtv-fw.c new file mode 100644 index 0000000000000..fe44789ab0379 --- /dev/null +++ b/drivers/media/dvb/firewire/firedtv-fw.c @@ -0,0 +1,376 @@ +/* + * FireDTV driver -- firewire I/O backend + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "firedtv.h" + +static LIST_HEAD(node_list); +static DEFINE_SPINLOCK(node_list_lock); + +static inline struct fw_device *device_of(struct firedtv *fdtv) +{ + return fw_device(fdtv->device->parent); +} + +static int node_req(struct firedtv *fdtv, u64 addr, void *data, size_t len, + int tcode) +{ + struct fw_device *device = device_of(fdtv); + int rcode, generation = device->generation; + + smp_rmb(); /* node_id vs. generation */ + + rcode = fw_run_transaction(device->card, tcode, device->node_id, + generation, device->max_speed, addr, data, len); + + return rcode != RCODE_COMPLETE ? -EIO : 0; +} + +static int node_lock(struct firedtv *fdtv, u64 addr, __be32 data[]) +{ + return node_req(fdtv, addr, data, 8, TCODE_LOCK_COMPARE_SWAP); +} + +static int node_read(struct firedtv *fdtv, u64 addr, void *data) +{ + return node_req(fdtv, addr, data, 4, TCODE_READ_QUADLET_REQUEST); +} + +static int node_write(struct firedtv *fdtv, u64 addr, void *data, size_t len) +{ + return node_req(fdtv, addr, data, len, TCODE_WRITE_BLOCK_REQUEST); +} + +#define ISO_HEADER_SIZE 4 +#define CIP_HEADER_SIZE 8 +#define MPEG2_TS_HEADER_SIZE 4 +#define MPEG2_TS_SOURCE_PACKET_SIZE (4 + 188) + +#define MAX_PACKET_SIZE 1024 /* 776, rounded up to 2^n */ +#define PACKETS_PER_PAGE (PAGE_SIZE / MAX_PACKET_SIZE) +#define N_PACKETS 64 /* buffer size */ +#define N_PAGES DIV_ROUND_UP(N_PACKETS, PACKETS_PER_PAGE) +#define IRQ_INTERVAL 16 + +struct firedtv_receive_context { + struct fw_iso_context *context; + struct fw_iso_buffer buffer; + int interrupt_packet; + int current_packet; + char *pages[N_PAGES]; +}; + +static int queue_iso(struct firedtv_receive_context *ctx, int index) +{ + struct fw_iso_packet p; + + p.payload_length = MAX_PACKET_SIZE; + p.interrupt = !(++ctx->interrupt_packet & (IRQ_INTERVAL - 1)); + p.skip = 0; + p.header_length = ISO_HEADER_SIZE; + + return fw_iso_context_queue(ctx->context, &p, &ctx->buffer, + index * MAX_PACKET_SIZE); +} + +static void handle_iso(struct fw_iso_context *context, u32 cycle, + size_t header_length, void *header, void *data) +{ + struct firedtv *fdtv = data; + struct firedtv_receive_context *ctx = fdtv->backend_data; + __be32 *h, *h_end; + int length, err, i = ctx->current_packet; + char *p, *p_end; + + for (h = header, h_end = h + header_length / 4; h < h_end; h++) { + length = be32_to_cpup(h) >> 16; + if (unlikely(length > MAX_PACKET_SIZE)) { + dev_err(fdtv->device, "length = %d\n", length); + length = MAX_PACKET_SIZE; + } + + p = ctx->pages[i / PACKETS_PER_PAGE] + + (i % PACKETS_PER_PAGE) * MAX_PACKET_SIZE; + p_end = p + length; + + for (p += CIP_HEADER_SIZE + MPEG2_TS_HEADER_SIZE; p < p_end; + p += MPEG2_TS_SOURCE_PACKET_SIZE) + dvb_dmx_swfilter_packets(&fdtv->demux, p, 1); + + err = queue_iso(ctx, i); + if (unlikely(err)) + dev_err(fdtv->device, "requeue failed\n"); + + i = (i + 1) & (N_PACKETS - 1); + } + ctx->current_packet = i; +} + +static int start_iso(struct firedtv *fdtv) +{ + struct firedtv_receive_context *ctx; + struct fw_device *device = device_of(fdtv); + int i, err; + + ctx = kmalloc(sizeof(*ctx), GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->context = fw_iso_context_create(device->card, + FW_ISO_CONTEXT_RECEIVE, fdtv->isochannel, + device->max_speed, ISO_HEADER_SIZE, handle_iso, fdtv); + if (IS_ERR(ctx->context)) { + err = PTR_ERR(ctx->context); + goto fail_free; + } + + err = fw_iso_buffer_init(&ctx->buffer, device->card, + N_PAGES, DMA_FROM_DEVICE); + if (err) + goto fail_context_destroy; + + ctx->interrupt_packet = 0; + ctx->current_packet = 0; + + for (i = 0; i < N_PAGES; i++) + ctx->pages[i] = page_address(ctx->buffer.pages[i]); + + for (i = 0; i < N_PACKETS; i++) { + err = queue_iso(ctx, i); + if (err) + goto fail; + } + + err = fw_iso_context_start(ctx->context, -1, 0, + FW_ISO_CONTEXT_MATCH_ALL_TAGS); + if (err) + goto fail; + + fdtv->backend_data = ctx; + + return 0; +fail: + fw_iso_buffer_destroy(&ctx->buffer, device->card); +fail_context_destroy: + fw_iso_context_destroy(ctx->context); +fail_free: + kfree(ctx); + + return err; +} + +static void stop_iso(struct firedtv *fdtv) +{ + struct firedtv_receive_context *ctx = fdtv->backend_data; + + fw_iso_context_stop(ctx->context); + fw_iso_buffer_destroy(&ctx->buffer, device_of(fdtv)->card); + fw_iso_context_destroy(ctx->context); + kfree(ctx); +} + +static const struct firedtv_backend backend = { + .lock = node_lock, + .read = node_read, + .write = node_write, + .start_iso = start_iso, + .stop_iso = stop_iso, +}; + +static void handle_fcp(struct fw_card *card, struct fw_request *request, + int tcode, int destination, int source, int generation, + int speed, unsigned long long offset, + void *payload, size_t length, void *callback_data) +{ + struct firedtv *f, *fdtv = NULL; + struct fw_device *device; + unsigned long flags; + int su; + + if ((tcode != TCODE_WRITE_QUADLET_REQUEST && + tcode != TCODE_WRITE_BLOCK_REQUEST) || + offset != CSR_REGISTER_BASE + CSR_FCP_RESPONSE || + length == 0 || + (((u8 *)payload)[0] & 0xf0) != 0) { + fw_send_response(card, request, RCODE_TYPE_ERROR); + return; + } + + su = ((u8 *)payload)[1] & 0x7; + + spin_lock_irqsave(&node_list_lock, flags); + list_for_each_entry(f, &node_list, list) { + device = device_of(f); + if (device->generation != generation) + continue; + + smp_rmb(); /* node_id vs. generation */ + + if (device->card == card && + device->node_id == source && + (f->subunit == su || (f->subunit == 0 && su == 0x7))) { + fdtv = f; + break; + } + } + spin_unlock_irqrestore(&node_list_lock, flags); + + if (fdtv) { + avc_recv(fdtv, payload, length); + fw_send_response(card, request, RCODE_COMPLETE); + } +} + +static struct fw_address_handler fcp_handler = { + .length = CSR_FCP_END - CSR_FCP_RESPONSE, + .address_callback = handle_fcp, +}; + +static const struct fw_address_region fcp_region = { + .start = CSR_REGISTER_BASE + CSR_FCP_RESPONSE, + .end = CSR_REGISTER_BASE + CSR_FCP_END, +}; + +/* Adjust the template string if models with longer names appear. */ +#define MAX_MODEL_NAME_LEN ((int)DIV_ROUND_UP(sizeof("FireDTV ????"), 4)) + +static size_t model_name(u32 *directory, __be32 *buffer) +{ + struct fw_csr_iterator ci; + int i, length, key, value, last_key = 0; + u32 *block = NULL; + + fw_csr_iterator_init(&ci, directory); + while (fw_csr_iterator_next(&ci, &key, &value)) { + if (last_key == CSR_MODEL && + key == (CSR_DESCRIPTOR | CSR_LEAF)) + block = ci.p - 1 + value; + last_key = key; + } + + if (block == NULL) + return 0; + + length = min((int)(block[0] >> 16) - 2, MAX_MODEL_NAME_LEN); + if (length <= 0) + return 0; + + /* fast-forward to text string */ + block += 3; + + for (i = 0; i < length; i++) + buffer[i] = cpu_to_be32(block[i]); + + return length * 4; +} + +static int node_probe(struct device *dev) +{ + struct firedtv *fdtv; + __be32 name[MAX_MODEL_NAME_LEN]; + int name_len, err; + + name_len = model_name(fw_unit(dev)->directory, name); + + fdtv = fdtv_alloc(dev, &backend, (char *)name, name_len); + if (!fdtv) + return -ENOMEM; + + err = fdtv_register_rc(fdtv, dev); + if (err) + goto fail_free; + + spin_lock_irq(&node_list_lock); + list_add_tail(&fdtv->list, &node_list); + spin_unlock_irq(&node_list_lock); + + err = avc_identify_subunit(fdtv); + if (err) + goto fail; + + err = fdtv_dvb_register(fdtv); + if (err) + goto fail; + + avc_register_remote_control(fdtv); + + return 0; +fail: + spin_lock_irq(&node_list_lock); + list_del(&fdtv->list); + spin_unlock_irq(&node_list_lock); + fdtv_unregister_rc(fdtv); +fail_free: + kfree(fdtv); + + return err; +} + +static int node_remove(struct device *dev) +{ + struct firedtv *fdtv = dev_get_drvdata(dev); + + fdtv_dvb_unregister(fdtv); + + spin_lock_irq(&node_list_lock); + list_del(&fdtv->list); + spin_unlock_irq(&node_list_lock); + + fdtv_unregister_rc(fdtv); + + kfree(fdtv); + return 0; +} + +static void node_update(struct fw_unit *unit) +{ + struct firedtv *fdtv = dev_get_drvdata(&unit->device); + + if (fdtv->isochannel >= 0) + cmp_establish_pp_connection(fdtv, fdtv->subunit, + fdtv->isochannel); +} + +static struct fw_driver fdtv_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "firedtv", + .bus = &fw_bus_type, + .probe = node_probe, + .remove = node_remove, + }, + .update = node_update, + .id_table = fdtv_id_table, +}; + +int __init fdtv_fw_init(void) +{ + int ret; + + ret = fw_core_add_address_handler(&fcp_handler, &fcp_region); + if (ret < 0) + return ret; + + return driver_register(&fdtv_driver.driver); +} + +void fdtv_fw_exit(void) +{ + driver_unregister(&fdtv_driver.driver); + fw_core_remove_address_handler(&fcp_handler); +} diff --git a/drivers/media/dvb/firewire/firedtv-rc.c b/drivers/media/dvb/firewire/firedtv-rc.c index 27bca2e283dfc..599d66e5843dc 100644 --- a/drivers/media/dvb/firewire/firedtv-rc.c +++ b/drivers/media/dvb/firewire/firedtv-rc.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "firedtv.h" @@ -163,6 +164,7 @@ int fdtv_register_rc(struct firedtv *fdtv, struct device *dev) void fdtv_unregister_rc(struct firedtv *fdtv) { + cancel_work_sync(&fdtv->remote_ctrl_work); kfree(fdtv->remote_ctrl_dev->keycode); input_unregister_device(fdtv->remote_ctrl_dev); } diff --git a/drivers/media/dvb/firewire/firedtv.h b/drivers/media/dvb/firewire/firedtv.h index d48530b81e618..35080dbb3c66d 100644 --- a/drivers/media/dvb/firewire/firedtv.h +++ b/drivers/media/dvb/firewire/firedtv.h @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -72,8 +73,8 @@ struct input_dev; struct firedtv; struct firedtv_backend { - int (*lock)(struct firedtv *fdtv, u64 addr, void *data, __be32 arg); - int (*read)(struct firedtv *fdtv, u64 addr, void *data, size_t len); + int (*lock)(struct firedtv *fdtv, u64 addr, __be32 data[]); + int (*read)(struct firedtv *fdtv, u64 addr, void *data); int (*write)(struct firedtv *fdtv, u64 addr, void *data, size_t len); int (*start_iso)(struct firedtv *fdtv); void (*stop_iso)(struct firedtv *fdtv); @@ -119,10 +120,10 @@ struct firedtv { /* firedtv-1394.c */ #ifdef CONFIG_DVB_FIREDTV_IEEE1394 -int fdtv_1394_init(struct ieee1394_device_id id_table[]); +int fdtv_1394_init(void); void fdtv_1394_exit(void); #else -static inline int fdtv_1394_init(struct ieee1394_device_id it[]) { return 0; } +static inline int fdtv_1394_init(void) { return 0; } static inline void fdtv_1394_exit(void) {} #endif @@ -163,10 +164,20 @@ struct firedtv *fdtv_alloc(struct device *dev, const struct firedtv_backend *backend, const char *name, size_t name_len); extern const char *fdtv_model_names[]; +extern const struct ieee1394_device_id fdtv_id_table[]; /* firedtv-fe.c */ void fdtv_frontend_init(struct firedtv *fdtv); +/* firedtv-fw.c */ +#ifdef CONFIG_DVB_FIREDTV_FIREWIRE +int fdtv_fw_init(void); +void fdtv_fw_exit(void); +#else +static inline int fdtv_fw_init(void) { return 0; } +static inline void fdtv_fw_exit(void) {} +#endif + /* firedtv-rc.c */ #ifdef CONFIG_DVB_FIREDTV_INPUT int fdtv_register_rc(struct firedtv *fdtv, struct device *dev); diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index d7c4837fa71cb..58aac018f1090 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -201,6 +201,13 @@ config DVB_SI21XX help A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_DS3000 + tristate "Montage Tehnology DS3000 based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DVB-S/S2 tuner module. Say Y when you want to support this frontend. + comment "DVB-T (terrestrial) frontends" depends on DVB_CORE @@ -342,6 +349,13 @@ config DVB_AF9013 help Say Y when you want to support this frontend. +config DVB_EC100 + tristate "E3C EC100" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + Say Y when you want to support this frontend. + comment "DVB-C (cable) frontends" depends on DVB_CORE @@ -557,6 +571,13 @@ config DVB_LGS8GXX help A DMB-TH tuner module. Say Y when you want to support this frontend. +config DVB_ATBM8830 + tristate "AltoBeam ATBM8830/8831 DMB-TH demodulator" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + A DMB-TH tuner module. Say Y when you want to support this frontend. + comment "Tools to develop new frontends" config DVB_DUMMY_FE diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile index 3523767e7a766..823482535d110 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -64,6 +64,7 @@ obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o obj-$(CONFIG_DVB_LGS8GL5) += lgs8gl5.o obj-$(CONFIG_DVB_LGS8GXX) += lgs8gxx.o +obj-$(CONFIG_DVB_ATBM8830) += atbm8830.o obj-$(CONFIG_DVB_DUMMY_FE) += dvb_dummy_fe.o obj-$(CONFIG_DVB_AF9013) += af9013.o obj-$(CONFIG_DVB_CX24116) += cx24116.o @@ -76,3 +77,5 @@ obj-$(CONFIG_DVB_STV0900) += stv0900.o obj-$(CONFIG_DVB_STV090x) += stv090x.o obj-$(CONFIG_DVB_STV6110x) += stv6110x.o obj-$(CONFIG_DVB_ISL6423) += isl6423.o +obj-$(CONFIG_DVB_EC100) += ec100.o +obj-$(CONFIG_DVB_DS3000) += ds3000.o diff --git a/drivers/media/dvb/frontends/atbm8830.c b/drivers/media/dvb/frontends/atbm8830.c new file mode 100644 index 0000000000000..59881a5944eb4 --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830.c @@ -0,0 +1,495 @@ +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include "dvb_frontend.h" + +#include "atbm8830.h" +#include "atbm8830_priv.h" + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG "atbm8830: " args); \ + } while (0) + +static int debug; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +static int atbm8830_write_reg(struct atbm_state *priv, u16 reg, u8 data) +{ + int ret = 0; + u8 dev_addr; + u8 buf1[] = { reg >> 8, reg & 0xFF }; + u8 buf2[] = { data }; + struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; + struct i2c_msg msg2 = { .flags = 0, .buf = buf2, .len = 1 }; + + dev_addr = priv->config->demod_address; + msg1.addr = dev_addr; + msg2.addr = dev_addr; + + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n", + __func__, reg, data); + + ret = i2c_transfer(priv->i2c, &msg1, 1); + if (ret != 1) + return -EIO; + + ret = i2c_transfer(priv->i2c, &msg2, 1); + return (ret != 1) ? -EIO : 0; +} + +static int atbm8830_read_reg(struct atbm_state *priv, u16 reg, u8 *p_data) +{ + int ret; + u8 dev_addr; + + u8 buf1[] = { reg >> 8, reg & 0xFF }; + u8 buf2[] = { 0 }; + struct i2c_msg msg1 = { .flags = 0, .buf = buf1, .len = 2 }; + struct i2c_msg msg2 = { .flags = I2C_M_RD, .buf = buf2, .len = 1 }; + + dev_addr = priv->config->demod_address; + msg1.addr = dev_addr; + msg2.addr = dev_addr; + + ret = i2c_transfer(priv->i2c, &msg1, 1); + if (ret != 1) { + dprintk(KERN_DEBUG "%s: error reg=0x%04x, ret=%i\n", + __func__, reg, ret); + return -EIO; + } + + ret = i2c_transfer(priv->i2c, &msg2, 1); + if (ret != 1) + return -EIO; + + *p_data = buf2[0]; + if (debug >= 2) + printk(KERN_DEBUG "%s: reg=0x%04X, data=0x%02X\n", + __func__, reg, buf2[0]); + + return 0; +} + +/* Lock register latch so that multi-register read is atomic */ +static inline int atbm8830_reglatch_lock(struct atbm_state *priv, int lock) +{ + return atbm8830_write_reg(priv, REG_READ_LATCH, lock ? 1 : 0); +} + +static int set_osc_freq(struct atbm_state *priv, u32 freq /*in kHz*/) +{ + u32 val; + u64 t; + + /* 0x100000 * freq / 30.4MHz */ + t = (u64)0x100000 * freq; + do_div(t, 30400); + val = t; + + atbm8830_write_reg(priv, REG_OSC_CLK, val); + atbm8830_write_reg(priv, REG_OSC_CLK + 1, val >> 8); + atbm8830_write_reg(priv, REG_OSC_CLK + 2, val >> 16); + + return 0; +} + +static int set_if_freq(struct atbm_state *priv, u32 freq /*in kHz*/) +{ + + u32 fs = priv->config->osc_clk_freq; + u64 t; + u32 val; + u8 dat; + + if (freq != 0) { + /* 2 * PI * (freq - fs) / fs * (2 ^ 22) */ + t = (u64) 2 * 31416 * (freq - fs); + t <<= 22; + do_div(t, fs); + do_div(t, 1000); + val = t; + + atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 1); + atbm8830_write_reg(priv, REG_IF_FREQ, val); + atbm8830_write_reg(priv, REG_IF_FREQ+1, val >> 8); + atbm8830_write_reg(priv, REG_IF_FREQ+2, val >> 16); + + atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); + dat &= 0xFC; + atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); + } else { + /* Zero IF */ + atbm8830_write_reg(priv, REG_TUNER_BASEBAND, 0); + + atbm8830_read_reg(priv, REG_ADC_CONFIG, &dat); + dat &= 0xFC; + dat |= 0x02; + atbm8830_write_reg(priv, REG_ADC_CONFIG, dat); + + if (priv->config->zif_swap_iq) + atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x03); + else + atbm8830_write_reg(priv, REG_SWAP_I_Q, 0x01); + } + + return 0; +} + +static int is_locked(struct atbm_state *priv, u8 *locked) +{ + u8 status; + + atbm8830_read_reg(priv, REG_LOCK_STATUS, &status); + + if (locked != NULL) + *locked = (status == 1); + return 0; +} + + +static int set_static_channel_mode(struct atbm_state *priv) +{ + int i; + + for (i = 0; i < 5; i++) + atbm8830_write_reg(priv, 0x099B + i, 0x08); + + atbm8830_write_reg(priv, 0x095B, 0x7F); + atbm8830_write_reg(priv, 0x09CB, 0x01); + atbm8830_write_reg(priv, 0x09CC, 0x7F); + atbm8830_write_reg(priv, 0x09CD, 0x7F); + atbm8830_write_reg(priv, 0x0E01, 0x20); + + /* For single carrier */ + atbm8830_write_reg(priv, 0x0B03, 0x0A); + atbm8830_write_reg(priv, 0x0935, 0x10); + atbm8830_write_reg(priv, 0x0936, 0x08); + atbm8830_write_reg(priv, 0x093E, 0x08); + atbm8830_write_reg(priv, 0x096E, 0x06); + + /* frame_count_max0 */ + atbm8830_write_reg(priv, 0x0B09, 0x00); + /* frame_count_max1 */ + atbm8830_write_reg(priv, 0x0B0A, 0x08); + + return 0; +} + +static int set_ts_config(struct atbm_state *priv) +{ + const struct atbm8830_config *cfg = priv->config; + + /*Set parallel/serial ts mode*/ + atbm8830_write_reg(priv, REG_TS_SERIAL, cfg->serial_ts ? 1 : 0); + atbm8830_write_reg(priv, REG_TS_CLK_MODE, cfg->serial_ts ? 1 : 0); + /*Set ts sampling edge*/ + atbm8830_write_reg(priv, REG_TS_SAMPLE_EDGE, + cfg->ts_sampling_edge ? 1 : 0); + /*Set ts clock freerun*/ + atbm8830_write_reg(priv, REG_TS_CLK_FREERUN, + cfg->ts_clk_gated ? 0 : 1); + + return 0; +} + +static int atbm8830_init(struct dvb_frontend *fe) +{ + struct atbm_state *priv = fe->demodulator_priv; + const struct atbm8830_config *cfg = priv->config; + + /*Set oscillator frequency*/ + set_osc_freq(priv, cfg->osc_clk_freq); + + /*Set IF frequency*/ + set_if_freq(priv, cfg->if_freq); + + + /*Set static channel mode*/ + set_static_channel_mode(priv); + + set_ts_config(priv); + /*Turn off DSP reset*/ + atbm8830_write_reg(priv, 0x000A, 0); + + /*SW version test*/ + atbm8830_write_reg(priv, 0x020C, 11); + + /* Run */ + atbm8830_write_reg(priv, REG_DEMOD_RUN, 1); + + return 0; +} + + +static void atbm8830_release(struct dvb_frontend *fe) +{ + struct atbm_state *state = fe->demodulator_priv; + dprintk("%s\n", __func__); + + kfree(state); +} + +static int atbm8830_set_fe(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fe_params) +{ + struct atbm_state *priv = fe->demodulator_priv; + int i; + u8 locked = 0; + dprintk("%s\n", __func__); + + /* set frequency */ + if (fe->ops.tuner_ops.set_params) { + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); + fe->ops.tuner_ops.set_params(fe, fe_params); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + } + + /* start auto lock */ + for (i = 0; i < 10; i++) { + mdelay(100); + dprintk("Try %d\n", i); + is_locked(priv, &locked); + if (locked != 0) { + dprintk("ATBM8830 locked!\n"); + break; + } + } + + return 0; +} + +static int atbm8830_get_fe(struct dvb_frontend *fe, + struct dvb_frontend_parameters *fe_params) +{ + dprintk("%s\n", __func__); + + /* TODO: get real readings from device */ + /* inversion status */ + fe_params->inversion = INVERSION_OFF; + + /* bandwidth */ + fe_params->u.ofdm.bandwidth = BANDWIDTH_8_MHZ; + + fe_params->u.ofdm.code_rate_HP = FEC_AUTO; + fe_params->u.ofdm.code_rate_LP = FEC_AUTO; + + fe_params->u.ofdm.constellation = QAM_AUTO; + + /* transmission mode */ + fe_params->u.ofdm.transmission_mode = TRANSMISSION_MODE_AUTO; + + /* guard interval */ + fe_params->u.ofdm.guard_interval = GUARD_INTERVAL_AUTO; + + /* hierarchy */ + fe_params->u.ofdm.hierarchy_information = HIERARCHY_NONE; + + return 0; +} + +static int atbm8830_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 0; + fesettings->step_size = 0; + fesettings->max_drift = 0; + return 0; +} + +static int atbm8830_read_status(struct dvb_frontend *fe, fe_status_t *fe_status) +{ + struct atbm_state *priv = fe->demodulator_priv; + u8 locked = 0; + u8 agc_locked = 0; + + dprintk("%s\n", __func__); + *fe_status = 0; + + is_locked(priv, &locked); + if (locked) { + *fe_status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + } + dprintk("%s: fe_status=0x%x\n", __func__, *fe_status); + + atbm8830_read_reg(priv, REG_AGC_LOCK, &agc_locked); + dprintk("AGC Lock: %d\n", agc_locked); + + return 0; +} + +static int atbm8830_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct atbm_state *priv = fe->demodulator_priv; + u32 frame_err; + u8 t; + + dprintk("%s\n", __func__); + + atbm8830_reglatch_lock(priv, 1); + + atbm8830_read_reg(priv, REG_FRAME_ERR_CNT + 1, &t); + frame_err = t & 0x7F; + frame_err <<= 8; + atbm8830_read_reg(priv, REG_FRAME_ERR_CNT, &t); + frame_err |= t; + + atbm8830_reglatch_lock(priv, 0); + + *ber = frame_err * 100 / 32767; + + dprintk("%s: ber=0x%x\n", __func__, *ber); + return 0; +} + +static int atbm8830_read_signal_strength(struct dvb_frontend *fe, u16 *signal) +{ + struct atbm_state *priv = fe->demodulator_priv; + u32 pwm; + u8 t; + + dprintk("%s\n", __func__); + atbm8830_reglatch_lock(priv, 1); + + atbm8830_read_reg(priv, REG_AGC_PWM_VAL + 1, &t); + pwm = t & 0x03; + pwm <<= 8; + atbm8830_read_reg(priv, REG_AGC_PWM_VAL, &t); + pwm |= t; + + atbm8830_reglatch_lock(priv, 0); + + dprintk("AGC PWM = 0x%02X\n", pwm); + pwm = 0x400 - pwm; + + *signal = pwm * 0x10000 / 0x400; + + return 0; +} + +static int atbm8830_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + dprintk("%s\n", __func__); + *snr = 0; + return 0; +} + +static int atbm8830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + dprintk("%s\n", __func__); + *ucblocks = 0; + return 0; +} + +static int atbm8830_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct atbm_state *priv = fe->demodulator_priv; + + return atbm8830_write_reg(priv, REG_I2C_GATE, enable ? 1 : 0); +} + +static struct dvb_frontend_ops atbm8830_ops = { + .info = { + .name = "AltoBeam ATBM8830/8831 DMB-TH", + .type = FE_OFDM, + .frequency_min = 474000000, + .frequency_max = 858000000, + .frequency_stepsize = 10000, + .caps = + FE_CAN_FEC_AUTO | + FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO + }, + + .release = atbm8830_release, + + .init = atbm8830_init, + .sleep = NULL, + .write = NULL, + .i2c_gate_ctrl = atbm8830_i2c_gate_ctrl, + + .set_frontend = atbm8830_set_fe, + .get_frontend = atbm8830_get_fe, + .get_tune_settings = atbm8830_get_tune_settings, + + .read_status = atbm8830_read_status, + .read_ber = atbm8830_read_ber, + .read_signal_strength = atbm8830_read_signal_strength, + .read_snr = atbm8830_read_snr, + .read_ucblocks = atbm8830_read_ucblocks, +}; + +struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c) +{ + struct atbm_state *priv = NULL; + u8 data = 0; + + dprintk("%s()\n", __func__); + + if (config == NULL || i2c == NULL) + return NULL; + + priv = kzalloc(sizeof(struct atbm_state), GFP_KERNEL); + if (priv == NULL) + goto error_out; + + priv->config = config; + priv->i2c = i2c; + + /* check if the demod is there */ + if (atbm8830_read_reg(priv, REG_CHIP_ID, &data) != 0) { + dprintk("%s atbm8830/8831 not found at i2c addr 0x%02X\n", + __func__, priv->config->demod_address); + goto error_out; + } + dprintk("atbm8830 chip id: 0x%02X\n", data); + + memcpy(&priv->frontend.ops, &atbm8830_ops, + sizeof(struct dvb_frontend_ops)); + priv->frontend.demodulator_priv = priv; + + atbm8830_init(&priv->frontend); + + atbm8830_i2c_gate_ctrl(&priv->frontend, 1); + + return &priv->frontend; + +error_out: + dprintk("%s() error_out\n", __func__); + kfree(priv); + return NULL; + +} +EXPORT_SYMBOL(atbm8830_attach); + +MODULE_DESCRIPTION("AltoBeam ATBM8830/8831 GB20600 demodulator driver"); +MODULE_AUTHOR("David T. L. Wong "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/atbm8830.h b/drivers/media/dvb/frontends/atbm8830.h new file mode 100644 index 0000000000000..e8149f393300e --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830.h @@ -0,0 +1,76 @@ +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ATBM8830_H__ +#define __ATBM8830_H__ + +#include +#include + +#define ATBM8830_PROD_8830 0 +#define ATBM8830_PROD_8831 1 + +struct atbm8830_config { + + /* product type */ + u8 prod; + + /* the demodulator's i2c address */ + u8 demod_address; + + /* parallel or serial transport stream */ + u8 serial_ts; + + /* transport stream clock output only when receving valid stream */ + u8 ts_clk_gated; + + /* Decoder sample TS data at rising edge of clock */ + u8 ts_sampling_edge; + + /* Oscillator clock frequency */ + u32 osc_clk_freq; /* in kHz */ + + /* IF frequency */ + u32 if_freq; /* in kHz */ + + /* Swap I/Q for zero IF */ + u8 zif_swap_iq; + + /* Tuner AGC settings */ + u8 agc_min; + u8 agc_max; + u8 agc_hold_loop; +}; + +#if defined(CONFIG_DVB_ATBM8830) || \ + (defined(CONFIG_DVB_ATBM8830_MODULE) && defined(MODULE)) +extern struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *atbm8830_attach(const struct atbm8830_config *config, + struct i2c_adapter *i2c) { + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_ATBM8830 */ + +#endif /* __ATBM8830_H__ */ diff --git a/drivers/media/dvb/frontends/atbm8830_priv.h b/drivers/media/dvb/frontends/atbm8830_priv.h new file mode 100644 index 0000000000000..ce960f76092af --- /dev/null +++ b/drivers/media/dvb/frontends/atbm8830_priv.h @@ -0,0 +1,75 @@ +/* + * Support for AltoBeam GB20600 (a.k.a DMB-TH) demodulator + * ATBM8830, ATBM8831 + * + * Copyright (C) 2009 David T.L. Wong + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ATBM8830_PRIV_H +#define __ATBM8830_PRIV_H + +struct atbm_state { + struct i2c_adapter *i2c; + /* configuration settings */ + const struct atbm8830_config *config; + struct dvb_frontend frontend; +}; + +#define REG_CHIP_ID 0x0000 +#define REG_TUNER_BASEBAND 0x0001 +#define REG_DEMOD_RUN 0x0004 +#define REG_DSP_RESET 0x0005 +#define REG_RAM_RESET 0x0006 +#define REG_ADC_RESET 0x0007 +#define REG_TSPORT_RESET 0x0008 +#define REG_BLKERR_POL 0x000C +#define REG_I2C_GATE 0x0103 +#define REG_TS_SAMPLE_EDGE 0x0301 +#define REG_TS_PKT_LEN_204 0x0302 +#define REG_TS_PKT_LEN_AUTO 0x0303 +#define REG_TS_SERIAL 0x0305 +#define REG_TS_CLK_FREERUN 0x0306 +#define REG_TS_VALID_MODE 0x0307 +#define REG_TS_CLK_MODE 0x030B /* 1 for serial, 0 for parallel */ + +#define REG_TS_ERRBIT_USE 0x030C +#define REG_LOCK_STATUS 0x030D +#define REG_ADC_CONFIG 0x0602 +#define REG_CARRIER_OFFSET 0x0827 /* 0x0827-0x0829 little endian */ +#define REG_DETECTED_PN_MODE 0x082D +#define REG_READ_LATCH 0x084D +#define REG_IF_FREQ 0x0A00 /* 0x0A00-0x0A02 little endian */ +#define REG_OSC_CLK 0x0A03 /* 0x0A03-0x0A05 little endian */ +#define REG_BYPASS_CCI 0x0A06 +#define REG_ANALOG_LUMA_DETECTED 0x0A25 +#define REG_ANALOG_AUDIO_DETECTED 0x0A26 +#define REG_ANALOG_CHROMA_DETECTED 0x0A39 +#define REG_FRAME_ERR_CNT 0x0B04 +#define REG_USE_EXT_ADC 0x0C00 +#define REG_SWAP_I_Q 0x0C01 +#define REG_TPS_MANUAL 0x0D01 +#define REG_TPS_CONFIG 0x0D02 +#define REG_BYPASS_DEINTERLEAVER 0x0E00 +#define REG_AGC_TARGET 0x1003 /* 0x1003-0x1005 little endian */ +#define REG_AGC_MIN 0x1020 +#define REG_AGC_MAX 0x1023 +#define REG_AGC_LOCK 0x1027 +#define REG_AGC_PWM_VAL 0x1028 /* 0x1028-0x1029 little endian */ +#define REG_AGC_HOLD_LOOP 0x1031 + +#endif + diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c index 7c6431fe33e00..2dc2723b724a1 100644 --- a/drivers/media/dvb/frontends/au8522_decoder.c +++ b/drivers/media/dvb/frontends/au8522_decoder.c @@ -23,7 +23,6 @@ /* Developer notes: * * VBI support is not yet working - * Saturation and hue setting are not yet working * Enough is implemented here for CVBS and S-Video inputs, but the actual * analog demodulator code isn't implemented (not needed for xc5000 since it * has its own demodulator and outputs CVBS) @@ -236,8 +235,10 @@ static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode) state->contrast = 0x79; au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80); au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80); + state->saturation = 0x80; au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00); au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00); + state->hue = 0x00; /* Other decoder registers */ au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00); @@ -504,7 +505,19 @@ static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ctrl->value); break; case V4L2_CID_SATURATION: + state->saturation = ctrl->value; + au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, + ctrl->value); + au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, + ctrl->value); + break; case V4L2_CID_HUE: + state->hue = ctrl->value; + au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, + ctrl->value >> 8); + au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, + ctrl->value & 0xFF); + break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: @@ -534,7 +547,11 @@ static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) ctrl->value = state->contrast; break; case V4L2_CID_SATURATION: + ctrl->value = state->saturation; + break; case V4L2_CID_HUE: + ctrl->value = state->hue; + break; case V4L2_CID_AUDIO_VOLUME: case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: @@ -632,8 +649,9 @@ static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) case V4L2_CID_BRIGHTNESS: return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); case V4L2_CID_HUE: - /* Not yet implemented */ + return v4l2_ctrl_query_fill(qc, -32768, 32768, 1, 0); default: break; } diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h index f328f2b3ad3d4..c74c4e72fe917 100644 --- a/drivers/media/dvb/frontends/au8522_priv.h +++ b/drivers/media/dvb/frontends/au8522_priv.h @@ -62,6 +62,8 @@ struct au8522_state { u32 rev; u8 brightness; u8 contrast; + u8 saturation; + s16 hue; }; /* These are routines shared by both the VSB/QAM demodulator and the analog diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 0781f94e05d2a..750ae61a20f4b 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -108,7 +108,7 @@ static int dib7000p_set_output_mode(struct dib7000p_state *state, int mode) outreg = 0; fifo_threshold = 1792; - smo_mode = (dib7000p_read_word(state, 235) & 0x0010) | (1 << 1); + smo_mode = (dib7000p_read_word(state, 235) & 0x0050) | (1 << 1); dprintk( "setting output mode for demod %p to %d", &state->demod, mode); @@ -162,18 +162,19 @@ static int dib7000p_set_diversity_in(struct dvb_frontend *demod, int onoff) if (state->div_force_off) { dprintk( "diversity combination deactivated - forced by COFDM parameters"); onoff = 0; - } + dib7000p_write_word(state, 207, 0); + } else + dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); + state->div_state = (u8)onoff; if (onoff) { dib7000p_write_word(state, 204, 6); dib7000p_write_word(state, 205, 16); /* P_dvsy_sync_mode = 0, P_dvsy_sync_enable=1, P_dvcb_comb_mode=2 */ - dib7000p_write_word(state, 207, (state->div_sync_wait << 4) | (1 << 2) | (2 << 0)); } else { dib7000p_write_word(state, 204, 1); dib7000p_write_word(state, 205, 0); - dib7000p_write_word(state, 207, 0); } return 0; @@ -1188,7 +1189,7 @@ static int dib7000p_read_status(struct dvb_frontend *fe, fe_status_t *stat) *stat |= FE_HAS_VITERBI; if (lock & 0x0010) *stat |= FE_HAS_SYNC; - if (lock & 0x0008) + if ((lock & 0x0038) == 0x38) *stat |= FE_HAS_LOCK; return 0; @@ -1302,6 +1303,24 @@ struct i2c_adapter * dib7000p_get_i2c_master(struct dvb_frontend *demod, enum di } EXPORT_SYMBOL(dib7000p_get_i2c_master); +int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + u16 val = dib7000p_read_word(state, 235) & 0xffef; + val |= (onoff & 0x1) << 4; + dprintk("PID filter enabled %d", onoff); + return dib7000p_write_word(state, 235, val); +} +EXPORT_SYMBOL(dib7000p_pid_filter_ctrl); + +int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib7000p_state *state = fe->demodulator_priv; + dprintk("PID filter: index %x, PID %d, OnOff %d", id, pid, onoff); + return dib7000p_write_word(state, 241 + id, onoff ? (1 << 13) | pid : 0); +} +EXPORT_SYMBOL(dib7000p_pid_filter); + int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib7000p_config cfg[]) { struct dib7000p_state st = { .i2c_adap = i2c }; @@ -1314,8 +1333,10 @@ int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 defau /* designated i2c address */ new_addr = (0x40 + k) << 1; st.i2c_addr = new_addr; + dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */ if (dib7000p_identify(&st) != 0) { st.i2c_addr = default_addr; + dib7000p_write_word(&st, 1287, 0x0003); /* sram lead in, rdy */ if (dib7000p_identify(&st) != 0) { dprintk("DiB7000P #%d: not identified\n", k); return -EIO; @@ -1372,6 +1393,8 @@ struct dvb_frontend * dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, demod->demodulator_priv = st; memcpy(&st->demod.ops, &dib7000p_ops, sizeof(struct dvb_frontend_ops)); + dib7000p_write_word(st, 1287, 0x0003); /* sram lead in, rdy */ + if (dib7000p_identify(st) != 0) goto error; diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h index 02a4c82f0c70c..805dd13a97ee3 100644 --- a/drivers/media/dvb/frontends/dib7000p.h +++ b/drivers/media/dvb/frontends/dib7000p.h @@ -51,6 +51,8 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c, extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value); extern int dib7000pc_detection(struct i2c_adapter *i2c_adap); +extern int dib7000p_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); +extern int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff); #else static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, @@ -95,6 +97,17 @@ static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap) printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } +static inline int dib7000p_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +static inline int dib7000p_pid_filter_ctrl(struct dvb_frontend *fe, uint8_t onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} #endif #endif diff --git a/drivers/media/dvb/frontends/dib8000.c b/drivers/media/dvb/frontends/dib8000.c index 852c790d09d97..898400d331a30 100644 --- a/drivers/media/dvb/frontends/dib8000.c +++ b/drivers/media/dvb/frontends/dib8000.c @@ -954,7 +954,7 @@ static void dib8000_set_channel(struct dib8000_state *state, u8 seq, u8 autosear u8 guard, crate, constellation, timeI; u8 permu_seg[] = { 6, 5, 7, 4, 8, 3, 9, 2, 10, 1, 11, 0, 12 }; u16 i, coeff[4], P_cfr_left_edge = 0, P_cfr_right_edge = 0, seg_mask13 = 0x1fff; // All 13 segments enabled - const s16 *ncoeff, *ana_fe; + const s16 *ncoeff = NULL, *ana_fe; u16 tmcc_pow = 0; u16 coff_pow = 0x2800; u16 init_prbs = 0xfff; @@ -2121,7 +2121,7 @@ static int dib8000_read_snr(struct dvb_frontend *fe, u16 * snr) else result -= intlog10(2) * 10 * noise_exp - 100; - *snr = result / (1 << 24); + *snr = result / ((1 << 24) / 10); return 0; } @@ -2195,6 +2195,25 @@ struct i2c_adapter *dib8000_get_i2c_master(struct dvb_frontend *fe, enum dibx000 EXPORT_SYMBOL(dib8000_get_i2c_master); +int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + struct dib8000_state *st = fe->demodulator_priv; + u16 val = dib8000_read_word(st, 299) & 0xffef; + val |= (onoff & 0x1) << 4; + + dprintk("pid filter enabled %d", onoff); + return dib8000_write_word(st, 299, val); +} +EXPORT_SYMBOL(dib8000_pid_filter_ctrl); + +int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + struct dib8000_state *st = fe->demodulator_priv; + dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); + return dib8000_write_word(st, 305 + id, onoff ? (1 << 13) | pid : 0); +} +EXPORT_SYMBOL(dib8000_pid_filter); + static const struct dvb_frontend_ops dib8000_ops = { .info = { .name = "DiBcom 8000 ISDB-T", diff --git a/drivers/media/dvb/frontends/dib8000.h b/drivers/media/dvb/frontends/dib8000.h index a86de340dd54c..8c89482b738aa 100644 --- a/drivers/media/dvb/frontends/dib8000.h +++ b/drivers/media/dvb/frontends/dib8000.h @@ -44,6 +44,8 @@ extern int dib8000_i2c_enumeration(struct i2c_adapter *host, int no_of_demods, u extern int dib8000_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val); extern int dib8000_set_wbd_ref(struct dvb_frontend *, u16 value); +extern int dib8000_pid_filter_ctrl(struct dvb_frontend *, u8 onoff); +extern int dib8000_pid_filter(struct dvb_frontend *, u8 id, u16 pid, u8 onoff); #else static inline struct dvb_frontend *dib8000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib8000_config *cfg) { @@ -74,6 +76,18 @@ int dib8000_set_wbd_ref(struct dvb_frontend *fe, u16 value) printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); return -ENODEV; } + +int dib8000_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} + +int dib8000_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return -ENODEV; +} #endif #endif diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c new file mode 100644 index 0000000000000..cff3535566fe2 --- /dev/null +++ b/drivers/media/dvb/frontends/ds3000.c @@ -0,0 +1,1367 @@ +/* + Montage Technology DS3000/TS2020 - DVBS/S2 Demodulator/Tuner driver + Copyright (C) 2009 Konstantin Dimitrov + + Copyright (C) 2009 TurboSight.com + + 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, or + (at your option) any later version. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "dvb_frontend.h" +#include "ds3000.h" + +static int debug; + +#define dprintk(args...) \ + do { \ + if (debug) \ + printk(args); \ + } while (0) + +/* as of March 2009 current DS3000 firmware version is 1.78 */ +/* DS3000 FW v1.78 MD5: a32d17910c4f370073f9346e71d34b80 */ +#define DS3000_DEFAULT_FIRMWARE "dvb-fe-ds3000.fw" + +#define DS3000_SAMPLE_RATE 96000 /* in kHz */ +#define DS3000_XTAL_FREQ 27000 /* in kHz */ + +/* Register values to initialise the demod in DVB-S mode */ +static u8 ds3000_dvbs_init_tab[] = { + 0x23, 0x05, + 0x08, 0x03, + 0x0c, 0x00, + 0x21, 0x54, + 0x25, 0x82, + 0x27, 0x31, + 0x30, 0x08, + 0x31, 0x40, + 0x32, 0x32, + 0x33, 0x35, + 0x35, 0xff, + 0x3a, 0x00, + 0x37, 0x10, + 0x38, 0x10, + 0x39, 0x02, + 0x42, 0x60, + 0x4a, 0x40, + 0x4b, 0x04, + 0x4d, 0x91, + 0x5d, 0xc8, + 0x50, 0x77, + 0x51, 0x77, + 0x52, 0x36, + 0x53, 0x36, + 0x56, 0x01, + 0x63, 0x43, + 0x64, 0x30, + 0x65, 0x40, + 0x68, 0x26, + 0x69, 0x4c, + 0x70, 0x20, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x40, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x60, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x80, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0xa0, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x1f, + 0x76, 0x00, + 0x77, 0xd1, + 0x78, 0x0c, + 0x79, 0x80, + 0x7f, 0x04, + 0x7c, 0x00, + 0x80, 0x86, + 0x81, 0xa6, + 0x85, 0x04, + 0xcd, 0xf4, + 0x90, 0x33, + 0xa0, 0x44, + 0xc0, 0x18, + 0xc3, 0x10, + 0xc4, 0x08, + 0xc5, 0x80, + 0xc6, 0x80, + 0xc7, 0x0a, + 0xc8, 0x1a, + 0xc9, 0x80, + 0xfe, 0x92, + 0xe0, 0xf8, + 0xe6, 0x8b, + 0xd0, 0x40, + 0xf8, 0x20, + 0xfa, 0x0f, + 0xfd, 0x20, + 0xad, 0x20, + 0xae, 0x07, + 0xb8, 0x00, +}; + +/* Register values to initialise the demod in DVB-S2 mode */ +static u8 ds3000_dvbs2_init_tab[] = { + 0x23, 0x0f, + 0x08, 0x07, + 0x0c, 0x00, + 0x21, 0x54, + 0x25, 0x82, + 0x27, 0x31, + 0x30, 0x08, + 0x31, 0x32, + 0x32, 0x32, + 0x33, 0x35, + 0x35, 0xff, + 0x3a, 0x00, + 0x37, 0x10, + 0x38, 0x10, + 0x39, 0x02, + 0x42, 0x60, + 0x4a, 0x80, + 0x4b, 0x04, + 0x4d, 0x81, + 0x5d, 0x88, + 0x50, 0x36, + 0x51, 0x36, + 0x52, 0x36, + 0x53, 0x36, + 0x63, 0x60, + 0x64, 0x10, + 0x65, 0x10, + 0x68, 0x04, + 0x69, 0x29, + 0x70, 0x20, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x40, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x60, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x80, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0xa0, + 0x71, 0x70, + 0x72, 0x04, + 0x73, 0x00, + 0x70, 0x1f, + 0xa0, 0x44, + 0xc0, 0x08, + 0xc1, 0x10, + 0xc2, 0x08, + 0xc3, 0x10, + 0xc4, 0x08, + 0xc5, 0xf0, + 0xc6, 0xf0, + 0xc7, 0x0a, + 0xc8, 0x1a, + 0xc9, 0x80, + 0xca, 0x23, + 0xcb, 0x24, + 0xce, 0x74, + 0x90, 0x03, + 0x76, 0x80, + 0x77, 0x42, + 0x78, 0x0a, + 0x79, 0x80, + 0xad, 0x40, + 0xae, 0x07, + 0x7f, 0xd4, + 0x7c, 0x00, + 0x80, 0xa8, + 0x81, 0xda, + 0x7c, 0x01, + 0x80, 0xda, + 0x81, 0xec, + 0x7c, 0x02, + 0x80, 0xca, + 0x81, 0xeb, + 0x7c, 0x03, + 0x80, 0xba, + 0x81, 0xdb, + 0x85, 0x08, + 0x86, 0x00, + 0x87, 0x02, + 0x89, 0x80, + 0x8b, 0x44, + 0x8c, 0xaa, + 0x8a, 0x10, + 0xba, 0x00, + 0xf5, 0x04, + 0xfe, 0x44, + 0xd2, 0x32, + 0xb8, 0x00, +}; + +/* DS3000 doesn't need some parameters as input and auto-detects them */ +/* save input from the application of those parameters */ +struct ds3000_tuning { + u32 frequency; + u32 symbol_rate; + fe_spectral_inversion_t inversion; + enum fe_code_rate fec; + + /* input values */ + u8 inversion_val; + fe_modulation_t delivery; + u8 rolloff; +}; + +struct ds3000_state { + struct i2c_adapter *i2c; + const struct ds3000_config *config; + + struct dvb_frontend frontend; + + struct ds3000_tuning dcur; + struct ds3000_tuning dnxt; + + u8 skip_fw_load; + + /* previous uncorrected block counter for DVB-S2 */ + u16 prevUCBS2; +}; + +static int ds3000_writereg(struct ds3000_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); + + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk(KERN_ERR "%s: writereg error(err == %i, reg == 0x%02x," + " value == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +static int ds3000_tuner_writereg(struct ds3000_state *state, int reg, int data) +{ + u8 buf[] = { reg, data }; + struct i2c_msg msg = { .addr = 0x60, + .flags = 0, .buf = buf, .len = 2 }; + int err; + + dprintk("%s: write reg 0x%02x, value 0x%02x\n", __func__, reg, data); + + ds3000_writereg(state, 0x03, 0x11); + err = i2c_transfer(state->i2c, &msg, 1); + if (err != 1) { + printk("%s: writereg error(err == %i, reg == 0x%02x," + " value == 0x%02x)\n", __func__, err, reg, data); + return -EREMOTEIO; + } + + return 0; +} + +/* I2C write for 8k firmware load */ +static int ds3000_writeFW(struct ds3000_state *state, int reg, + const u8 *data, u16 len) +{ + int i, ret = -EREMOTEIO; + struct i2c_msg msg; + u8 *buf; + + buf = kmalloc(3, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "Unable to kmalloc\n"); + ret = -ENOMEM; + goto error; + } + + *(buf) = reg; + + msg.addr = state->config->demod_address; + msg.flags = 0; + msg.buf = buf; + msg.len = 3; + + for (i = 0; i < len; i += 2) { + memcpy(buf + 1, data + i, 2); + + dprintk("%s: write reg 0x%02x, len = %d\n", __func__, reg, len); + + ret = i2c_transfer(state->i2c, &msg, 1); + if (ret != 1) { + printk(KERN_ERR "%s: write error(err == %i, " + "reg == 0x%02x\n", __func__, ret, reg); + ret = -EREMOTEIO; + } + } + +error: + kfree(buf); + + return ret; +} + +static int ds3000_readreg(struct ds3000_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = state->config->demod_address, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = state->config->demod_address, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); + return ret; + } + + dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); + + return b1[0]; +} + +static int ds3000_tuner_readreg(struct ds3000_state *state, u8 reg) +{ + int ret; + u8 b0[] = { reg }; + u8 b1[] = { 0 }; + struct i2c_msg msg[] = { + { + .addr = 0x60, + .flags = 0, + .buf = b0, + .len = 1 + }, { + .addr = 0x60, + .flags = I2C_M_RD, + .buf = b1, + .len = 1 + } + }; + + ds3000_writereg(state, 0x03, 0x12); + ret = i2c_transfer(state->i2c, msg, 2); + + if (ret != 2) { + printk(KERN_ERR "%s: reg=0x%x(error=%d)\n", __func__, reg, ret); + return ret; + } + + dprintk("%s: read reg 0x%02x, value 0x%02x\n", __func__, reg, b1[0]); + + return b1[0]; +} + +static int ds3000_set_inversion(struct ds3000_state *state, + fe_spectral_inversion_t inversion) +{ + dprintk("%s(%d)\n", __func__, inversion); + + switch (inversion) { + case INVERSION_OFF: + case INVERSION_ON: + case INVERSION_AUTO: + break; + default: + return -EINVAL; + } + + state->dnxt.inversion = inversion; + + return 0; +} + +static int ds3000_set_symbolrate(struct ds3000_state *state, u32 rate) +{ + int ret = 0; + + dprintk("%s()\n", __func__); + + dprintk("%s() symbol_rate = %d\n", __func__, state->dnxt.symbol_rate); + + /* check if symbol rate is within limits */ + if ((state->dnxt.symbol_rate > + state->frontend.ops.info.symbol_rate_max) || + (state->dnxt.symbol_rate < + state->frontend.ops.info.symbol_rate_min)) + ret = -EOPNOTSUPP; + + state->dnxt.symbol_rate = rate; + + return ret; +} + +static int ds3000_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw); + +static int ds3000_firmware_ondemand(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + const struct firmware *fw; + int ret = 0; + + dprintk("%s()\n", __func__); + + if (ds3000_readreg(state, 0xb2) <= 0) + return ret; + + if (state->skip_fw_load) + return 0; + /* Load firmware */ + /* request the firmware, this will block until someone uploads it */ + printk(KERN_INFO "%s: Waiting for firmware upload (%s)...\n", __func__, + DS3000_DEFAULT_FIRMWARE); + ret = request_firmware(&fw, DS3000_DEFAULT_FIRMWARE, + state->i2c->dev.parent); + printk(KERN_INFO "%s: Waiting for firmware upload(2)...\n", __func__); + if (ret) { + printk(KERN_ERR "%s: No firmware uploaded (timeout or file not " + "found?)\n", __func__); + return ret; + } + + /* Make sure we don't recurse back through here during loading */ + state->skip_fw_load = 1; + + ret = ds3000_load_firmware(fe, fw); + if (ret) + printk("%s: Writing firmware to device failed\n", __func__); + + release_firmware(fw); + + dprintk("%s: Firmware upload %s\n", __func__, + ret == 0 ? "complete" : "failed"); + + /* Ensure firmware is always loaded if required */ + state->skip_fw_load = 0; + + return ret; +} + +static int ds3000_load_firmware(struct dvb_frontend *fe, + const struct firmware *fw) +{ + struct ds3000_state *state = fe->demodulator_priv; + + dprintk("%s\n", __func__); + dprintk("Firmware is %zu bytes (%02x %02x .. %02x %02x)\n", + fw->size, + fw->data[0], + fw->data[1], + fw->data[fw->size - 2], + fw->data[fw->size - 1]); + + /* Begin the firmware load process */ + ds3000_writereg(state, 0xb2, 0x01); + /* write the entire firmware */ + ds3000_writeFW(state, 0xb0, fw->data, fw->size); + ds3000_writereg(state, 0xb2, 0x00); + + return 0; +} + +static void ds3000_dump_registers(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + int x, y, reg = 0, val; + + for (y = 0; y < 16; y++) { + dprintk("%s: %02x: ", __func__, y); + for (x = 0; x < 16; x++) { + reg = (y << 4) + x; + val = ds3000_readreg(state, reg); + if (x != 15) + dprintk("%02x ", val); + else + dprintk("%02x\n", val); + } + } + dprintk("%s: -- DS3000 DUMP DONE --\n", __func__); +} + +static int ds3000_read_status(struct dvb_frontend *fe, fe_status_t* status) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int lock; + + *status = 0; + + switch (c->delivery_system) { + case SYS_DVBS: + lock = ds3000_readreg(state, 0xd1); + if ((lock & 0x07) == 0x07) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + + break; + case SYS_DVBS2: + lock = ds3000_readreg(state, 0x0d); + if ((lock & 0x8f) == 0x8f) + *status = FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + + break; + default: + return 1; + } + + dprintk("%s: status = 0x%02x\n", __func__, lock); + + return 0; +} + +#define FE_IS_TUNED (FE_HAS_SIGNAL + FE_HAS_LOCK) +static int ds3000_is_tuned(struct dvb_frontend *fe) +{ + fe_status_t tunerstat; + + ds3000_read_status(fe, &tunerstat); + + return ((tunerstat & FE_IS_TUNED) == FE_IS_TUNED); +} + +/* read DS3000 BER value */ +static int ds3000_read_ber(struct dvb_frontend *fe, u32* ber) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 data; + u32 ber_reading, lpdc_frames; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + /* set the number of bytes checked during + BER estimation */ + ds3000_writereg(state, 0xf9, 0x04); + /* read BER estimation status */ + data = ds3000_readreg(state, 0xf8); + /* check if BER estimation is ready */ + if ((data & 0x10) == 0) { + /* this is the number of error bits, + to calculate the bit error rate + divide to 8388608 */ + *ber = (ds3000_readreg(state, 0xf7) << 8) | + ds3000_readreg(state, 0xf6); + /* start counting error bits */ + /* need to be set twice + otherwise it fails sometimes */ + data |= 0x10; + ds3000_writereg(state, 0xf8, data); + ds3000_writereg(state, 0xf8, data); + } else + /* used to indicate that BER estimation + is not ready, i.e. BER is unknown */ + *ber = 0xffffffff; + break; + case SYS_DVBS2: + /* read the number of LPDC decoded frames */ + lpdc_frames = (ds3000_readreg(state, 0xd7) << 16) | + (ds3000_readreg(state, 0xd6) << 8) | + ds3000_readreg(state, 0xd5); + /* read the number of packets with bad CRC */ + ber_reading = (ds3000_readreg(state, 0xf8) << 8) | + ds3000_readreg(state, 0xf7); + if (lpdc_frames > 750) { + /* clear LPDC frame counters */ + ds3000_writereg(state, 0xd1, 0x01); + /* clear bad packets counter */ + ds3000_writereg(state, 0xf9, 0x01); + /* enable bad packets counter */ + ds3000_writereg(state, 0xf9, 0x00); + /* enable LPDC frame counters */ + ds3000_writereg(state, 0xd1, 0x00); + *ber = ber_reading; + } else + /* used to indicate that BER estimation is not ready, + i.e. BER is unknown */ + *ber = 0xffffffff; + break; + default: + return 1; + } + + return 0; +} + +/* read TS2020 signal strength */ +static int ds3000_read_signal_strength(struct dvb_frontend *fe, + u16 *signal_strength) +{ + struct ds3000_state *state = fe->demodulator_priv; + u16 sig_reading, sig_strength; + u8 rfgain, bbgain; + + dprintk("%s()\n", __func__); + + rfgain = ds3000_tuner_readreg(state, 0x3d) & 0x1f; + bbgain = ds3000_tuner_readreg(state, 0x21) & 0x1f; + + if (rfgain > 15) + rfgain = 15; + if (bbgain > 13) + bbgain = 13; + + sig_reading = rfgain * 2 + bbgain * 3; + + sig_strength = 40 + (64 - sig_reading) * 50 / 64 ; + + /* cook the value to be suitable for szap-s2 human readable output */ + *signal_strength = sig_strength * 1000; + + dprintk("%s: raw / cooked = 0x%04x / 0x%04x\n", __func__, + sig_reading, *signal_strength); + + return 0; +} + +/* calculate DS3000 snr value in dB */ +static int ds3000_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 snr_reading, snr_value; + u32 dvbs2_signal_reading, dvbs2_noise_reading, tmp; + static const u16 dvbs_snr_tab[] = { /* 20 x Table (rounded up) */ + 0x0000, 0x1b13, 0x2aea, 0x3627, 0x3ede, 0x45fe, 0x4c03, + 0x513a, 0x55d4, 0x59f2, 0x5dab, 0x6111, 0x6431, 0x6717, + 0x69c9, 0x6c4e, 0x6eac, 0x70e8, 0x7304, 0x7505 + }; + static const u16 dvbs2_snr_tab[] = { /* 80 x Table (rounded up) */ + 0x0000, 0x0bc2, 0x12a3, 0x1785, 0x1b4e, 0x1e65, 0x2103, + 0x2347, 0x2546, 0x2710, 0x28ae, 0x2a28, 0x2b83, 0x2cc5, + 0x2df1, 0x2f09, 0x3010, 0x3109, 0x31f4, 0x32d2, 0x33a6, + 0x3470, 0x3531, 0x35ea, 0x369b, 0x3746, 0x37ea, 0x3888, + 0x3920, 0x39b3, 0x3a42, 0x3acc, 0x3b51, 0x3bd3, 0x3c51, + 0x3ccb, 0x3d42, 0x3db6, 0x3e27, 0x3e95, 0x3f00, 0x3f68, + 0x3fcf, 0x4033, 0x4094, 0x40f4, 0x4151, 0x41ac, 0x4206, + 0x425e, 0x42b4, 0x4308, 0x435b, 0x43ac, 0x43fc, 0x444a, + 0x4497, 0x44e2, 0x452d, 0x4576, 0x45bd, 0x4604, 0x4649, + 0x468e, 0x46d1, 0x4713, 0x4755, 0x4795, 0x47d4, 0x4813, + 0x4851, 0x488d, 0x48c9, 0x4904, 0x493f, 0x4978, 0x49b1, + 0x49e9, 0x4a20, 0x4a57 + }; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + snr_reading = ds3000_readreg(state, 0xff); + snr_reading /= 8; + if (snr_reading == 0) + *snr = 0x0000; + else { + if (snr_reading > 20) + snr_reading = 20; + snr_value = dvbs_snr_tab[snr_reading - 1] * 10 / 23026; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = snr_value * 8 * 655; + } + dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, + snr_reading, *snr); + break; + case SYS_DVBS2: + dvbs2_noise_reading = (ds3000_readreg(state, 0x8c) & 0x3f) + + (ds3000_readreg(state, 0x8d) << 4); + dvbs2_signal_reading = ds3000_readreg(state, 0x8e); + tmp = dvbs2_signal_reading * dvbs2_signal_reading >> 1; + if (dvbs2_signal_reading == 0) { + *snr = 0x0000; + return 0; + } + if (dvbs2_noise_reading == 0) { + snr_value = 0x0013; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = 0xffff; + return 0; + } + if (tmp > dvbs2_noise_reading) { + snr_reading = tmp / dvbs2_noise_reading; + if (snr_reading > 80) + snr_reading = 80; + snr_value = dvbs2_snr_tab[snr_reading - 1] / 1000; + /* cook the value to be suitable for szap-s2 + human readable output */ + *snr = snr_value * 5 * 655; + } else { + snr_reading = dvbs2_noise_reading / tmp; + if (snr_reading > 80) + snr_reading = 80; + *snr = -(dvbs2_snr_tab[snr_reading] / 1000); + } + dprintk("%s: raw / cooked = 0x%02x / 0x%04x\n", __func__, + snr_reading, *snr); + break; + default: + return 1; + } + + return 0; +} + +/* read DS3000 uncorrected blocks */ +static int ds3000_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + u8 data; + u16 _ucblocks; + + dprintk("%s()\n", __func__); + + switch (c->delivery_system) { + case SYS_DVBS: + *ucblocks = (ds3000_readreg(state, 0xf5) << 8) | + ds3000_readreg(state, 0xf4); + data = ds3000_readreg(state, 0xf8); + /* clear packet counters */ + data &= ~0x20; + ds3000_writereg(state, 0xf8, data); + /* enable packet counters */ + data |= 0x20; + ds3000_writereg(state, 0xf8, data); + break; + case SYS_DVBS2: + _ucblocks = (ds3000_readreg(state, 0xe2) << 8) | + ds3000_readreg(state, 0xe1); + if (_ucblocks > state->prevUCBS2) + *ucblocks = _ucblocks - state->prevUCBS2; + else + *ucblocks = state->prevUCBS2 - _ucblocks; + state->prevUCBS2 = _ucblocks; + break; + default: + return 1; + } + + return 0; +} + +/* Overwrite the current tuning params, we are about to tune */ +static void ds3000_clone_params(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + memcpy(&state->dcur, &state->dnxt, sizeof(state->dcur)); +} + +static int ds3000_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +{ + struct ds3000_state *state = fe->demodulator_priv; + u8 data; + + dprintk("%s(%d)\n", __func__, tone); + if ((tone != SEC_TONE_ON) && (tone != SEC_TONE_OFF)) { + printk(KERN_ERR "%s: Invalid, tone=%d\n", __func__, tone); + return -EINVAL; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + switch (tone) { + case SEC_TONE_ON: + dprintk("%s: setting tone on\n", __func__); + data = ds3000_readreg(state, 0xa1); + data &= ~0x43; + data |= 0x04; + ds3000_writereg(state, 0xa1, data); + break; + case SEC_TONE_OFF: + dprintk("%s: setting tone off\n", __func__); + data = ds3000_readreg(state, 0xa2); + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + break; + } + + return 0; +} + +static int ds3000_send_diseqc_msg(struct dvb_frontend *fe, + struct dvb_diseqc_master_cmd *d) +{ + struct ds3000_state *state = fe->demodulator_priv; + int i; + u8 data; + + /* Dump DiSEqC message */ + dprintk("%s(", __func__); + for (i = 0 ; i < d->msg_len;) { + dprintk("0x%02x", d->msg[i]); + if (++i < d->msg_len) + dprintk(", "); + } + + /* enable DiSEqC message send pin */ + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + /* DiSEqC message */ + for (i = 0; i < d->msg_len; i++) + ds3000_writereg(state, 0xa3 + i, d->msg[i]); + + data = ds3000_readreg(state, 0xa1); + /* clear DiSEqC message length and status, + enable DiSEqC message send */ + data &= ~0xf8; + /* set DiSEqC mode, modulation active during 33 pulses, + set DiSEqC message length */ + data |= ((d->msg_len - 1) << 3) | 0x07; + ds3000_writereg(state, 0xa1, data); + + /* wait up to 150ms for DiSEqC transmission to complete */ + for (i = 0; i < 15; i++) { + data = ds3000_readreg(state, 0xa1); + if ((data & 0x40) == 0) + break; + msleep(10); + } + + /* DiSEqC timeout after 150ms */ + if (i == 15) { + data = ds3000_readreg(state, 0xa1); + data &= ~0x80; + data |= 0x40; + ds3000_writereg(state, 0xa1, data); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 1; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 0; +} + +/* Send DiSEqC burst */ +static int ds3000_diseqc_send_burst(struct dvb_frontend *fe, + fe_sec_mini_cmd_t burst) +{ + struct ds3000_state *state = fe->demodulator_priv; + int i; + u8 data; + + dprintk("%s()\n", __func__); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + ds3000_writereg(state, 0xa2, data); + + /* DiSEqC burst */ + if (burst == SEC_MINI_A) + /* Unmodulated tone burst */ + ds3000_writereg(state, 0xa1, 0x02); + else if (burst == SEC_MINI_B) + /* Modulated tone burst */ + ds3000_writereg(state, 0xa1, 0x01); + else + return -EINVAL; + + msleep(13); + for (i = 0; i < 5; i++) { + data = ds3000_readreg(state, 0xa1); + if ((data & 0x40) == 0) + break; + msleep(1); + } + + if (i == 5) { + data = ds3000_readreg(state, 0xa1); + data &= ~0x80; + data |= 0x40; + ds3000_writereg(state, 0xa1, data); + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 1; + } + + data = ds3000_readreg(state, 0xa2); + data &= ~0xc0; + data |= 0x80; + ds3000_writereg(state, 0xa2, data); + + return 0; +} + +static void ds3000_release(struct dvb_frontend *fe) +{ + struct ds3000_state *state = fe->demodulator_priv; + dprintk("%s\n", __func__); + kfree(state); +} + +static struct dvb_frontend_ops ds3000_ops; + +struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c) +{ + struct ds3000_state *state = NULL; + int ret; + + dprintk("%s\n", __func__); + + /* allocate memory for the internal state */ + state = kmalloc(sizeof(struct ds3000_state), GFP_KERNEL); + if (state == NULL) { + printk(KERN_ERR "Unable to kmalloc\n"); + goto error2; + } + + /* setup the state */ + memset(state, 0, sizeof(struct ds3000_state)); + + state->config = config; + state->i2c = i2c; + state->prevUCBS2 = 0; + + /* check if the demod is present */ + ret = ds3000_readreg(state, 0x00) & 0xfe; + if (ret != 0xe0) { + printk(KERN_ERR "Invalid probe, probably not a DS3000\n"); + goto error3; + } + + printk(KERN_INFO "DS3000 chip version: %d.%d attached.\n", + ds3000_readreg(state, 0x02), + ds3000_readreg(state, 0x01)); + + memcpy(&state->frontend.ops, &ds3000_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + return &state->frontend; + +error3: + kfree(state); +error2: + return NULL; +} +EXPORT_SYMBOL(ds3000_attach); + +static int ds3000_set_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int ds3000_get_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + dprintk("%s(..)\n", __func__); + return 0; +} + +static int ds3000_tune(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct ds3000_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + + int ret = 0, retune, i; + u8 status, mlpf, mlpf_new, mlpf_max, mlpf_min, nlpf; + u16 value, ndiv; + u32 f3db; + + dprintk("%s() ", __func__); + + /* Load the firmware if required */ + ret = ds3000_firmware_ondemand(fe); + if (ret != 0) { + printk(KERN_ERR "%s: Unable initialise the firmware\n", + __func__); + return ret; + } + + state->dnxt.delivery = c->modulation; + state->dnxt.frequency = c->frequency; + state->dnxt.rolloff = 2; /* fixme */ + state->dnxt.fec = c->fec_inner; + + ret = ds3000_set_inversion(state, p->inversion); + if (ret != 0) + return ret; + + ret = ds3000_set_symbolrate(state, c->symbol_rate); + if (ret != 0) + return ret; + + /* discard the 'current' tuning parameters and prepare to tune */ + ds3000_clone_params(fe); + + retune = 1; /* try 1 times */ + dprintk("%s: retune = %d\n", __func__, retune); + dprintk("%s: frequency = %d\n", __func__, state->dcur.frequency); + dprintk("%s: symbol_rate = %d\n", __func__, state->dcur.symbol_rate); + dprintk("%s: FEC = %d \n", __func__, + state->dcur.fec); + dprintk("%s: Inversion = %d\n", __func__, state->dcur.inversion); + + do { + /* Reset status register */ + status = 0; + /* Tune */ + /* TS2020 init */ + ds3000_tuner_writereg(state, 0x42, 0x73); + ds3000_tuner_writereg(state, 0x05, 0x01); + ds3000_tuner_writereg(state, 0x62, 0xf5); + /* unknown */ + ds3000_tuner_writereg(state, 0x07, 0x02); + ds3000_tuner_writereg(state, 0x10, 0x00); + ds3000_tuner_writereg(state, 0x60, 0x79); + ds3000_tuner_writereg(state, 0x08, 0x01); + ds3000_tuner_writereg(state, 0x00, 0x01); + /* calculate and set freq divider */ + if (state->dcur.frequency < 1146000) { + ds3000_tuner_writereg(state, 0x10, 0x11); + ndiv = ((state->dcur.frequency * (6 + 8) * 4) + + (DS3000_XTAL_FREQ / 2)) / + DS3000_XTAL_FREQ - 1024; + } else { + ds3000_tuner_writereg(state, 0x10, 0x01); + ndiv = ((state->dcur.frequency * (6 + 8) * 2) + + (DS3000_XTAL_FREQ / 2)) / + DS3000_XTAL_FREQ - 1024; + } + + ds3000_tuner_writereg(state, 0x01, (ndiv & 0x0f00) >> 8); + ds3000_tuner_writereg(state, 0x02, ndiv & 0x00ff); + + /* set pll */ + ds3000_tuner_writereg(state, 0x03, 0x06); + ds3000_tuner_writereg(state, 0x51, 0x0f); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x10); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + /* unknown */ + ds3000_tuner_writereg(state, 0x51, 0x17); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x08); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + value = ds3000_tuner_readreg(state, 0x3d); + value &= 0x0f; + if ((value > 4) && (value < 15)) { + value -= 3; + if (value < 4) + value = 4; + value = ((value << 3) | 0x01) & 0x79; + } + + ds3000_tuner_writereg(state, 0x60, value); + ds3000_tuner_writereg(state, 0x51, 0x17); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x08); + ds3000_tuner_writereg(state, 0x50, 0x00); + + /* set low-pass filter period */ + ds3000_tuner_writereg(state, 0x04, 0x2e); + ds3000_tuner_writereg(state, 0x51, 0x1b); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x04); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + f3db = ((state->dcur.symbol_rate / 1000) << 2) / 5 + 2000; + if ((state->dcur.symbol_rate / 1000) < 5000) + f3db += 3000; + if (f3db < 7000) + f3db = 7000; + if (f3db > 40000) + f3db = 40000; + + /* set low-pass filter baseband */ + value = ds3000_tuner_readreg(state, 0x26); + mlpf = 0x2e * 207 / ((value << 1) + 151); + mlpf_max = mlpf * 135 / 100; + mlpf_min = mlpf * 78 / 100; + if (mlpf_max > 63) + mlpf_max = 63; + + /* rounded to the closest integer */ + nlpf = ((mlpf * f3db * 1000) + (2766 * DS3000_XTAL_FREQ / 2)) + / (2766 * DS3000_XTAL_FREQ); + if (nlpf > 23) + nlpf = 23; + if (nlpf < 1) + nlpf = 1; + + /* rounded to the closest integer */ + mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + + if (mlpf_new < mlpf_min) { + nlpf++; + mlpf_new = ((DS3000_XTAL_FREQ * nlpf * 2766) + + (1000 * f3db / 2)) / (1000 * f3db); + } + + if (mlpf_new > mlpf_max) + mlpf_new = mlpf_max; + + ds3000_tuner_writereg(state, 0x04, mlpf_new); + ds3000_tuner_writereg(state, 0x06, nlpf); + ds3000_tuner_writereg(state, 0x51, 0x1b); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x04); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(5); + + /* unknown */ + ds3000_tuner_writereg(state, 0x51, 0x1e); + ds3000_tuner_writereg(state, 0x51, 0x1f); + ds3000_tuner_writereg(state, 0x50, 0x01); + ds3000_tuner_writereg(state, 0x50, 0x00); + msleep(60); + + /* ds3000 global reset */ + ds3000_writereg(state, 0x07, 0x80); + ds3000_writereg(state, 0x07, 0x00); + /* ds3000 build-in uC reset */ + ds3000_writereg(state, 0xb2, 0x01); + /* ds3000 software reset */ + ds3000_writereg(state, 0x00, 0x01); + + switch (c->delivery_system) { + case SYS_DVBS: + /* initialise the demod in DVB-S mode */ + for (i = 0; i < sizeof(ds3000_dvbs_init_tab); i += 2) + ds3000_writereg(state, + ds3000_dvbs_init_tab[i], + ds3000_dvbs_init_tab[i + 1]); + value = ds3000_readreg(state, 0xfe); + value &= 0xc0; + value |= 0x1b; + ds3000_writereg(state, 0xfe, value); + break; + case SYS_DVBS2: + /* initialise the demod in DVB-S2 mode */ + for (i = 0; i < sizeof(ds3000_dvbs2_init_tab); i += 2) + ds3000_writereg(state, + ds3000_dvbs2_init_tab[i], + ds3000_dvbs2_init_tab[i + 1]); + ds3000_writereg(state, 0xfe, 0x54); + break; + default: + return 1; + } + + /* enable 27MHz clock output */ + ds3000_writereg(state, 0x29, 0x80); + /* enable ac coupling */ + ds3000_writereg(state, 0x25, 0x8a); + + /* enhance symbol rate performance */ + if ((state->dcur.symbol_rate / 1000) <= 5000) { + value = 29777 / (state->dcur.symbol_rate / 1000) + 1; + if (value % 2 != 0) + value++; + ds3000_writereg(state, 0xc3, 0x0d); + ds3000_writereg(state, 0xc8, value); + ds3000_writereg(state, 0xc4, 0x10); + ds3000_writereg(state, 0xc7, 0x0e); + } else if ((state->dcur.symbol_rate / 1000) <= 10000) { + value = 92166 / (state->dcur.symbol_rate / 1000) + 1; + if (value % 2 != 0) + value++; + ds3000_writereg(state, 0xc3, 0x07); + ds3000_writereg(state, 0xc8, value); + ds3000_writereg(state, 0xc4, 0x09); + ds3000_writereg(state, 0xc7, 0x12); + } else if ((state->dcur.symbol_rate / 1000) <= 20000) { + value = 64516 / (state->dcur.symbol_rate / 1000) + 1; + ds3000_writereg(state, 0xc3, value); + ds3000_writereg(state, 0xc8, 0x0e); + ds3000_writereg(state, 0xc4, 0x07); + ds3000_writereg(state, 0xc7, 0x18); + } else { + value = 129032 / (state->dcur.symbol_rate / 1000) + 1; + ds3000_writereg(state, 0xc3, value); + ds3000_writereg(state, 0xc8, 0x0a); + ds3000_writereg(state, 0xc4, 0x05); + ds3000_writereg(state, 0xc7, 0x24); + } + + /* normalized symbol rate rounded to the closest integer */ + value = (((state->dcur.symbol_rate / 1000) << 16) + + (DS3000_SAMPLE_RATE / 2)) / DS3000_SAMPLE_RATE; + ds3000_writereg(state, 0x61, value & 0x00ff); + ds3000_writereg(state, 0x62, (value & 0xff00) >> 8); + + /* co-channel interference cancellation disabled */ + ds3000_writereg(state, 0x56, 0x00); + + /* equalizer disabled */ + ds3000_writereg(state, 0x76, 0x00); + + /*ds3000_writereg(state, 0x08, 0x03); + ds3000_writereg(state, 0xfd, 0x22); + ds3000_writereg(state, 0x08, 0x07); + ds3000_writereg(state, 0xfd, 0x42); + ds3000_writereg(state, 0x08, 0x07);*/ + + /* ds3000 out of software reset */ + ds3000_writereg(state, 0x00, 0x00); + /* start ds3000 build-in uC */ + ds3000_writereg(state, 0xb2, 0x00); + + /* TODO: calculate and set carrier offset */ + + /* wait before retrying */ + for (i = 0; i < 30 ; i++) { + if (ds3000_is_tuned(fe)) { + dprintk("%s: Tuned\n", __func__); + ds3000_dump_registers(fe); + goto tuned; + } + msleep(1); + } + + dprintk("%s: Not tuned\n", __func__); + ds3000_dump_registers(fe); + + } while (--retune); + +tuned: + return ret; +} + +static enum dvbfe_algo ds3000_get_algo(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return DVBFE_ALGO_SW; +} + +/* + * Initialise or wake up device + * + * Power config will reset and load initial firmware if required + */ +static int ds3000_initfe(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +/* Put device to sleep */ +static int ds3000_sleep(struct dvb_frontend *fe) +{ + dprintk("%s()\n", __func__); + return 0; +} + +static struct dvb_frontend_ops ds3000_ops = { + + .info = { + .name = "Montage Technology DS3000/TS2020", + .type = FE_QPSK, + .frequency_min = 950000, + .frequency_max = 2150000, + .frequency_stepsize = 1011, /* kHz for QPSK frontends */ + .frequency_tolerance = 5000, + .symbol_rate_min = 1000000, + .symbol_rate_max = 45000000, + .caps = FE_CAN_INVERSION_AUTO | + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_4_5 | FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | + FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_2G_MODULATION | + FE_CAN_QPSK | FE_CAN_RECOVER + }, + + .release = ds3000_release, + + .init = ds3000_initfe, + .sleep = ds3000_sleep, + .read_status = ds3000_read_status, + .read_ber = ds3000_read_ber, + .read_signal_strength = ds3000_read_signal_strength, + .read_snr = ds3000_read_snr, + .read_ucblocks = ds3000_read_ucblocks, + .set_tone = ds3000_set_tone, + .diseqc_send_master_cmd = ds3000_send_diseqc_msg, + .diseqc_send_burst = ds3000_diseqc_send_burst, + .get_frontend_algo = ds3000_get_algo, + + .set_property = ds3000_set_property, + .get_property = ds3000_get_property, + .set_frontend = ds3000_tune, +}; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Activates frontend debugging (default:0)"); + +MODULE_DESCRIPTION("DVB Frontend module for Montage Technology " + "DS3000/TS2020 hardware"); +MODULE_AUTHOR("Konstantin Dimitrov"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/ds3000.h b/drivers/media/dvb/frontends/ds3000.h new file mode 100644 index 0000000000000..67f67038740a5 --- /dev/null +++ b/drivers/media/dvb/frontends/ds3000.h @@ -0,0 +1,45 @@ +/* + Montage Technology DS3000/TS2020 - DVBS/S2 Satellite demod/tuner driver + Copyright (C) 2009 Konstantin Dimitrov + + Copyright (C) 2009 TurboSight.com + + 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, or + (at your option) any later version. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +#ifndef DS3000_H +#define DS3000_H + +#include + +struct ds3000_config { + /* the demodulator's i2c address */ + u8 demod_address; +}; + +#if defined(CONFIG_DVB_DS3000) || \ + (defined(CONFIG_DVB_DS3000_MODULE) && defined(MODULE)) +extern struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c); +#else +static inline +struct dvb_frontend *ds3000_attach(const struct ds3000_config *config, + struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_DS3000 */ +#endif /* DS3000_H */ diff --git a/drivers/media/dvb/frontends/ec100.c b/drivers/media/dvb/frontends/ec100.c new file mode 100644 index 0000000000000..2414dc6ee5d99 --- /dev/null +++ b/drivers/media/dvb/frontends/ec100.c @@ -0,0 +1,335 @@ +/* + * E3C EC100 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "dvb_frontend.h" +#include "ec100_priv.h" +#include "ec100.h" + +int ec100_debug; +module_param_named(debug, ec100_debug, int, 0644); +MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off)."); + +struct ec100_state { + struct i2c_adapter *i2c; + struct dvb_frontend frontend; + struct ec100_config config; + + u16 ber; +}; + +/* write single register */ +static int ec100_write_reg(struct ec100_state *state, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = state->config.demod_address, + .flags = 0, + .len = 2, + .buf = buf}; + + if (i2c_transfer(state->i2c, &msg, 1) != 1) { + warn("I2C write failed reg:%02x", reg); + return -EREMOTEIO; + } + return 0; +} + +/* read single register */ +static int ec100_read_reg(struct ec100_state *state, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { + .addr = state->config.demod_address, + .flags = 0, + .len = 1, + .buf = ® + }, { + .addr = state->config.demod_address, + .flags = I2C_M_RD, + .len = 1, + .buf = val + } + }; + + if (i2c_transfer(state->i2c, msg, 2) != 2) { + warn("I2C read failed reg:%02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int ec100_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *params) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp, tmp2; + + deb_info("%s: freq:%d bw:%d\n", __func__, params->frequency, + params->u.ofdm.bandwidth); + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe, params); + + ret = ec100_write_reg(state, 0x04, 0x06); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x67, 0x58); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x05, 0x18); + if (ret) + goto error; + + /* reg/bw | 6 | 7 | 8 + -------+------+------+------ + A 0x1b | 0xa1 | 0xe7 | 0x2c + A 0x1c | 0x55 | 0x63 | 0x72 + -------+------+------+------ + B 0x1b | 0xb7 | 0x00 | 0x49 + B 0x1c | 0x55 | 0x64 | 0x72 */ + + switch (params->u.ofdm.bandwidth) { + case BANDWIDTH_6_MHZ: + tmp = 0xb7; + tmp2 = 0x55; + break; + case BANDWIDTH_7_MHZ: + tmp = 0x00; + tmp2 = 0x64; + break; + case BANDWIDTH_8_MHZ: + default: + tmp = 0x49; + tmp2 = 0x72; + } + + ret = ec100_write_reg(state, 0x1b, tmp); + if (ret) + goto error; + ret = ec100_write_reg(state, 0x1c, tmp2); + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x0c, 0xbb); /* if freq */ + if (ret) + goto error; + ret = ec100_write_reg(state, 0x0d, 0x31); /* if freq */ + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x08, 0x24); + if (ret) + goto error; + + ret = ec100_write_reg(state, 0x00, 0x00); /* go */ + if (ret) + goto error; + ret = ec100_write_reg(state, 0x00, 0x20); /* go */ + if (ret) + goto error; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec100_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 300; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int ec100_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + *status = 0; + + ret = ec100_read_reg(state, 0x42, &tmp); + if (ret) + goto error; + + if (tmp & 0x80) { + /* bit7 set - have lock */ + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | FE_HAS_VITERBI | + FE_HAS_SYNC | FE_HAS_LOCK; + } else { + ret = ec100_read_reg(state, 0x01, &tmp); + if (ret) + goto error; + + if (tmp & 0x10) { + /* bit4 set - have signal */ + *status |= FE_HAS_SIGNAL; + if (!(tmp & 0x01)) { + /* bit0 clear - have ~valid signal */ + *status |= FE_HAS_CARRIER | FE_HAS_VITERBI; + } + } + } + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec100_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp, tmp2; + u16 ber2; + + *ber = 0; + + ret = ec100_read_reg(state, 0x65, &tmp); + if (ret) + goto error; + ret = ec100_read_reg(state, 0x66, &tmp2); + if (ret) + goto error; + + ber2 = (tmp2 << 8) | tmp; + + /* if counter overflow or clear */ + if (ber2 < state->ber) + *ber = ber2; + else + *ber = ber2 - state->ber; + + state->ber = ber2; + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec100_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct ec100_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + + ret = ec100_read_reg(state, 0x24, &tmp); + if (ret) { + *strength = 0; + goto error; + } + + *strength = ((tmp << 8) | tmp); + + return ret; +error: + deb_info("%s: failed:%d\n", __func__, ret); + return ret; +} + +static int ec100_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + *snr = 0; + return 0; +} + +static int ec100_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + *ucblocks = 0; + return 0; +} + +static void ec100_release(struct dvb_frontend *fe) +{ + struct ec100_state *state = fe->demodulator_priv; + kfree(state); +} + +static struct dvb_frontend_ops ec100_ops; + +struct dvb_frontend *ec100_attach(const struct ec100_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct ec100_state *state = NULL; + u8 tmp; + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct ec100_state), GFP_KERNEL); + if (state == NULL) + goto error; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->config, config, sizeof(struct ec100_config)); + + /* check if the demod is there */ + ret = ec100_read_reg(state, 0x33, &tmp); + if (ret || tmp != 0x0b) + goto error; + + /* create dvb_frontend */ + memcpy(&state->frontend.ops, &ec100_ops, + sizeof(struct dvb_frontend_ops)); + state->frontend.demodulator_priv = state; + + return &state->frontend; +error: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(ec100_attach); + +static struct dvb_frontend_ops ec100_ops = { + .info = { + .name = "E3C EC100 DVB-T", + .type = FE_OFDM, + .caps = + FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 | + FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO | + FE_CAN_QPSK | FE_CAN_QAM_16 | + FE_CAN_QAM_64 | FE_CAN_QAM_AUTO | + FE_CAN_TRANSMISSION_MODE_AUTO | + FE_CAN_GUARD_INTERVAL_AUTO | + FE_CAN_HIERARCHY_AUTO | + FE_CAN_MUTE_TS + }, + + .release = ec100_release, + .set_frontend = ec100_set_frontend, + .get_tune_settings = ec100_get_tune_settings, + .read_status = ec100_read_status, + .read_ber = ec100_read_ber, + .read_signal_strength = ec100_read_signal_strength, + .read_snr = ec100_read_snr, + .read_ucblocks = ec100_read_ucblocks, +}; + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("E3C EC100 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/ec100.h b/drivers/media/dvb/frontends/ec100.h new file mode 100644 index 0000000000000..ee8e52417958e --- /dev/null +++ b/drivers/media/dvb/frontends/ec100.h @@ -0,0 +1,46 @@ +/* + * E3C EC100 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef EC100_H +#define EC100_H + +#include + +struct ec100_config { + /* demodulator's I2C address */ + u8 demod_address; +}; + + +#if defined(CONFIG_DVB_EC100) || \ + (defined(CONFIG_DVB_EC100_MODULE) && defined(MODULE)) +extern struct dvb_frontend *ec100_attach(const struct ec100_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *ec100_attach( + const struct ec100_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* EC100_H */ diff --git a/drivers/media/dvb/frontends/ec100_priv.h b/drivers/media/dvb/frontends/ec100_priv.h new file mode 100644 index 0000000000000..5c990144bc472 --- /dev/null +++ b/drivers/media/dvb/frontends/ec100_priv.h @@ -0,0 +1,39 @@ +/* + * E3C EC100 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#ifndef EC100_PRIV +#define EC100_PRIV + +#define LOG_PREFIX "ec100" + +#define dprintk(var, level, args...) \ + do { if ((var & level)) printk(args); } while (0) + +#define deb_info(args...) dprintk(ec100_debug, 0x01, args) + +#undef err +#define err(f, arg...) printk(KERN_ERR LOG_PREFIX": " f "\n" , ## arg) +#undef info +#define info(f, arg...) printk(KERN_INFO LOG_PREFIX": " f "\n" , ## arg) +#undef warn +#define warn(f, arg...) printk(KERN_WARNING LOG_PREFIX": " f "\n" , ## arg) + +#endif /* EC100_PRIV */ diff --git a/drivers/media/dvb/frontends/s5h1409.c b/drivers/media/dvb/frontends/s5h1409.c index fb30115184270..0e2f61a8978ff 100644 --- a/drivers/media/dvb/frontends/s5h1409.c +++ b/drivers/media/dvb/frontends/s5h1409.c @@ -44,7 +44,15 @@ struct s5h1409_state { int if_freq; u32 is_qam_locked; - u32 qam_state; + + /* QAM tuning state goes through the following state transitions */ +#define QAM_STATE_UNTUNED 0 +#define QAM_STATE_TUNING_STARTED 1 +#define QAM_STATE_INTERLEAVE_SET 2 +#define QAM_STATE_QAM_OPTIMIZED_L1 3 +#define QAM_STATE_QAM_OPTIMIZED_L2 4 +#define QAM_STATE_QAM_OPTIMIZED_L3 5 + u8 qam_state; }; static int debug; @@ -347,7 +355,7 @@ static int s5h1409_softreset(struct dvb_frontend *fe) s5h1409_writereg(state, 0xf5, 0); s5h1409_writereg(state, 0xf5, 1); state->is_qam_locked = 0; - state->qam_state = 0; + state->qam_state = QAM_STATE_UNTUNED; return 0; } @@ -474,6 +482,59 @@ static void s5h1409_set_qam_amhum_mode(struct dvb_frontend *fe) struct s5h1409_state *state = fe->demodulator_priv; u16 reg; + if (state->qam_state < QAM_STATE_INTERLEAVE_SET) { + /* We should not perform amhum optimization until + the interleave mode has been configured */ + return; + } + + if (state->qam_state == QAM_STATE_QAM_OPTIMIZED_L3) { + /* We've already reached the maximum optimization level, so + dont bother banging on the status registers */ + return; + } + + /* QAM EQ lock check */ + reg = s5h1409_readreg(state, 0xf0); + + if ((reg >> 13) & 0x1) { + reg &= 0xff; + + s5h1409_writereg(state, 0x96, 0x000c); + if (reg < 0x68) { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L3) { + dprintk("%s() setting QAM state to OPT_L3\n", + __func__); + s5h1409_writereg(state, 0x93, 0x3130); + s5h1409_writereg(state, 0x9e, 0x2836); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L3; + } + } else { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L2) { + dprintk("%s() setting QAM state to OPT_L2\n", + __func__); + s5h1409_writereg(state, 0x93, 0x3332); + s5h1409_writereg(state, 0x9e, 0x2c37); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L2; + } + } + + } else { + if (state->qam_state < QAM_STATE_QAM_OPTIMIZED_L1) { + dprintk("%s() setting QAM state to OPT_L1\n", __func__); + s5h1409_writereg(state, 0x96, 0x0008); + s5h1409_writereg(state, 0x93, 0x3332); + s5h1409_writereg(state, 0x9e, 0x2c37); + state->qam_state = QAM_STATE_QAM_OPTIMIZED_L1; + } + } +} + +static void s5h1409_set_qam_amhum_mode_legacy(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg; + if (state->is_qam_locked) return; @@ -506,6 +567,44 @@ static void s5h1409_set_qam_interleave_mode(struct dvb_frontend *fe) struct s5h1409_state *state = fe->demodulator_priv; u16 reg, reg1, reg2; + if (state->qam_state >= QAM_STATE_INTERLEAVE_SET) { + /* We've done the optimization already */ + return; + } + + reg = s5h1409_readreg(state, 0xf1); + + /* Master lock */ + if ((reg >> 15) & 0x1) { + if (state->qam_state == QAM_STATE_UNTUNED || + state->qam_state == QAM_STATE_TUNING_STARTED) { + dprintk("%s() setting QAM state to INTERLEAVE_SET\n", + __func__); + reg1 = s5h1409_readreg(state, 0xb2); + reg2 = s5h1409_readreg(state, 0xad); + + s5h1409_writereg(state, 0x96, 0x0020); + s5h1409_writereg(state, 0xad, + (((reg1 & 0xf000) >> 4) | (reg2 & 0xf0ff))); + state->qam_state = QAM_STATE_INTERLEAVE_SET; + } + } else { + if (state->qam_state == QAM_STATE_UNTUNED) { + dprintk("%s() setting QAM state to TUNING_STARTED\n", + __func__); + s5h1409_writereg(state, 0x96, 0x08); + s5h1409_writereg(state, 0xab, + s5h1409_readreg(state, 0xab) | 0x1001); + state->qam_state = QAM_STATE_TUNING_STARTED; + } + } +} + +static void s5h1409_set_qam_interleave_mode_legacy(struct dvb_frontend *fe) +{ + struct s5h1409_state *state = fe->demodulator_priv; + u16 reg, reg1, reg2; + reg = s5h1409_readreg(state, 0xf1); /* Master lock */ @@ -553,16 +652,24 @@ static int s5h1409_set_frontend(struct dvb_frontend *fe, fe->ops.i2c_gate_ctrl(fe, 0); } - /* Optimize the demod for QAM */ - if (p->u.vsb.modulation != VSB_8) { - s5h1409_set_qam_amhum_mode(fe); - s5h1409_set_qam_interleave_mode(fe); - } - /* Issue a reset to the demod so it knows to resync against the newly tuned frequency */ s5h1409_softreset(fe); + /* Optimize the demod for QAM */ + if (state->current_modulation != VSB_8) { + /* This almost certainly applies to all boards, but for now + only do it for the HVR-1600. Once the other boards are + tested, the "legacy" versions can just go away */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + s5h1409_set_qam_interleave_mode(fe); + s5h1409_set_qam_amhum_mode(fe); + } else { + s5h1409_set_qam_amhum_mode_legacy(fe); + s5h1409_set_qam_interleave_mode_legacy(fe); + } + } + return 0; } @@ -614,6 +721,21 @@ static int s5h1409_init(struct dvb_frontend *fe) /* The datasheet says that after initialisation, VSB is default */ state->current_modulation = VSB_8; + /* Optimize for the HVR-1600 if appropriate. Note that some of these + may get folded into the generic case after testing with other + devices */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + /* VSB AGC REF */ + s5h1409_writereg(state, 0x09, 0x0050); + + /* Unknown but Windows driver does it... */ + s5h1409_writereg(state, 0x21, 0x0001); + s5h1409_writereg(state, 0x50, 0x030e); + + /* QAM AGC REF */ + s5h1409_writereg(state, 0x82, 0x0800); + } + if (state->config->output_mode == S5H1409_SERIAL_OUTPUT) s5h1409_writereg(state, 0xab, s5h1409_readreg(state, 0xab) | 0x100); /* Serial */ @@ -641,6 +763,17 @@ static int s5h1409_read_status(struct dvb_frontend *fe, fe_status_t *status) *status = 0; + /* Optimize the demod for QAM */ + if (state->current_modulation != VSB_8) { + /* This almost certainly applies to all boards, but for now + only do it for the HVR-1600. Once the other boards are + tested, the "legacy" versions can just go away */ + if (state->config->hvr1600_opt == S5H1409_HVR1600_OPTIMIZE) { + s5h1409_set_qam_interleave_mode(fe); + s5h1409_set_qam_amhum_mode(fe); + } + } + /* Get the demodulator status */ reg = s5h1409_readreg(state, 0xf1); if (reg & 0x1000) diff --git a/drivers/media/dvb/frontends/s5h1409.h b/drivers/media/dvb/frontends/s5h1409.h index 070d9743e330c..91f2ebd1a534e 100644 --- a/drivers/media/dvb/frontends/s5h1409.h +++ b/drivers/media/dvb/frontends/s5h1409.h @@ -57,6 +57,13 @@ struct s5h1409_config { #define S5H1409_MPEGTIMING_NONCONTINOUS_INVERTING_CLOCK 2 #define S5H1409_MPEGTIMING_NONCONTINOUS_NONINVERTING_CLOCK 3 u16 mpeg_timing; + + /* HVR-1600 optimizations (to better work with MXL5005s) + Note: some of these are likely to be folded into the generic driver + after being regression tested with other boards */ +#define S5H1409_HVR1600_NOOPTIMIZE 0 +#define S5H1409_HVR1600_OPTIMIZE 1 + u8 hvr1600_opt; }; #if defined(CONFIG_DVB_S5H1409) || (defined(CONFIG_DVB_S5H1409_MODULE) \ diff --git a/drivers/media/dvb/frontends/stb6100_proc.h b/drivers/media/dvb/frontends/stb6100_proc.h new file mode 100644 index 0000000000000..112163a486223 --- /dev/null +++ b/drivers/media/dvb/frontends/stb6100_proc.h @@ -0,0 +1,138 @@ +/* + STB6100 Silicon Tuner wrapper + Copyright (C)2009 Igor M. Liplianin (liplianin@me.by) + + 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, or + (at your option) any later version. + + 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., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + +static int stb6100_get_freq(struct dvb_frontend *fe, u32 *frequency) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state state; + int err = 0; + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_state) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->get_state(fe, DVBFE_TUNER_FREQUENCY, &state); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + *frequency = state.frequency; + } + + return 0; +} + +static int stb6100_set_freq(struct dvb_frontend *fe, u32 frequency) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state state; + int err = 0; + + state.frequency = frequency; + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->set_state(fe, DVBFE_TUNER_FREQUENCY, &state); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + } + + return 0; +} + +static int stb6100_get_bandw(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state state; + int err = 0; + + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->get_state) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->get_state(fe, DVBFE_TUNER_BANDWIDTH, &state); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + *bandwidth = state.bandwidth; + } + + return 0; +} + +static int stb6100_set_bandw(struct dvb_frontend *fe, u32 bandwidth) +{ + struct dvb_frontend_ops *frontend_ops = NULL; + struct dvb_tuner_ops *tuner_ops = NULL; + struct tuner_state state; + int err = 0; + + state.bandwidth = bandwidth; + if (&fe->ops) + frontend_ops = &fe->ops; + if (&frontend_ops->tuner_ops) + tuner_ops = &frontend_ops->tuner_ops; + if (tuner_ops->set_state) { + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 1); + + err = tuner_ops->set_state(fe, DVBFE_TUNER_BANDWIDTH, &state); + if (err < 0) { + printk(KERN_ERR "%s: Invalid parameter\n", __func__); + return err; + } + + if (frontend_ops->i2c_gate_ctrl) + frontend_ops->i2c_gate_ctrl(fe, 0); + + } + + return 0; +} diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h index bf4e9b6330444..29c3fa85c2276 100644 --- a/drivers/media/dvb/frontends/stv0900.h +++ b/drivers/media/dvb/frontends/stv0900.h @@ -36,6 +36,7 @@ struct stv0900_reg { struct stv0900_config { u8 demod_address; + u8 demod_mode; u32 xtal; u8 clkmode;/* 0 for CLKI, 2 for XTALI */ @@ -48,6 +49,8 @@ struct stv0900_config { u8 tun2_maddress; u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */ u8 tun2_adc; + /* Set device param to start dma */ + int (*set_ts_params)(struct dvb_frontend *fe, int is_punctured); }; #if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \ diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c index 3bde3324a032b..df49ea0983bc0 100644 --- a/drivers/media/dvb/frontends/stv0900_core.c +++ b/drivers/media/dvb/frontends/stv0900_core.c @@ -34,7 +34,7 @@ #include "stv0900_priv.h" #include "stv0900_init.h" -static int stvdebug = 1; +int stvdebug = 1; module_param_named(debug, stvdebug, int, 0644); /* internal params node */ @@ -105,7 +105,8 @@ static struct stv0900_inode *append_internal(struct stv0900_internal *internal) while (new_node->next_inode != NULL) new_node = new_node->next_inode; - new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL); + new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), + GFP_KERNEL); if (new_node->next_inode != NULL) new_node = new_node->next_inode; else @@ -128,13 +129,13 @@ s32 ge2comp(s32 a, s32 width) return (a >= (1 << (width - 1))) ? (a - (1 << width)) : a; } -void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr, +void stv0900_write_reg(struct stv0900_internal *intp, u16 reg_addr, u8 reg_data) { u8 data[3]; int ret; struct i2c_msg i2cmsg = { - .addr = i_params->i2c_addr, + .addr = intp->i2c_addr, .flags = 0, .len = 3, .buf = data, @@ -144,33 +145,33 @@ void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr, data[1] = LSB(reg_addr); data[2] = reg_data; - ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1); + ret = i2c_transfer(intp->i2c_adap, &i2cmsg, 1); if (ret != 1) - dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret); + dprintk("%s: i2c error %d\n", __func__, ret); } -u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg) +u8 stv0900_read_reg(struct stv0900_internal *intp, u16 reg) { int ret; u8 b0[] = { MSB(reg), LSB(reg) }; u8 buf = 0; struct i2c_msg msg[] = { { - .addr = i_params->i2c_addr, + .addr = intp->i2c_addr, .flags = 0, .buf = b0, .len = 2, }, { - .addr = i_params->i2c_addr, + .addr = intp->i2c_addr, .flags = I2C_M_RD, .buf = &buf, .len = 1, }, }; - ret = i2c_transfer(i_params->i2c_adap, msg, 2); + ret = i2c_transfer(intp->i2c_adap, msg, 2); if (ret != 2) - dprintk(KERN_ERR "%s: i2c error %d, reg[0x%02x]\n", + dprintk("%s: i2c error %d, reg[0x%02x]\n", __func__, ret, reg); return buf; @@ -190,169 +191,165 @@ void extract_mask_pos(u32 label, u8 *mask, u8 *pos) (*pos) = (i - 1); } -void stv0900_write_bits(struct stv0900_internal *i_params, u32 label, u8 val) +void stv0900_write_bits(struct stv0900_internal *intp, u32 label, u8 val) { u8 reg, mask, pos; - reg = stv0900_read_reg(i_params, (label >> 16) & 0xffff); + reg = stv0900_read_reg(intp, (label >> 16) & 0xffff); extract_mask_pos(label, &mask, &pos); val = mask & (val << pos); reg = (reg & (~mask)) | val; - stv0900_write_reg(i_params, (label >> 16) & 0xffff, reg); + stv0900_write_reg(intp, (label >> 16) & 0xffff, reg); } -u8 stv0900_get_bits(struct stv0900_internal *i_params, u32 label) +u8 stv0900_get_bits(struct stv0900_internal *intp, u32 label) { u8 val = 0xff; u8 mask, pos; extract_mask_pos(label, &mask, &pos); - val = stv0900_read_reg(i_params, label >> 16); + val = stv0900_read_reg(intp, label >> 16); val = (val & mask) >> pos; return val; } -enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *i_params) +enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *intp) { s32 i; - enum fe_stv0900_error error; - - if (i_params != NULL) { - i_params->chip_id = stv0900_read_reg(i_params, R0900_MID); - if (i_params->errs == STV0900_NO_ERROR) { - /*Startup sequence*/ - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5c); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5c); - stv0900_write_reg(i_params, R0900_P1_TNRCFG, 0x6c); - stv0900_write_reg(i_params, R0900_P2_TNRCFG, 0x6f); - stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x20); - stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x20); - stv0900_write_reg(i_params, R0900_NCOARSE, 0x13); - msleep(3); - stv0900_write_reg(i_params, R0900_I2CCFG, 0x08); - - switch (i_params->clkmode) { - case 0: - case 2: - stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20 - | i_params->clkmode); - break; - default: - /* preserve SELOSCI bit */ - i = 0x02 & stv0900_read_reg(i_params, R0900_SYNTCTRL); - stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20 | i); - break; - } - msleep(3); - for (i = 0; i < 182; i++) - stv0900_write_reg(i_params, STV0900_InitVal[i][0], STV0900_InitVal[i][1]); + if (intp == NULL) + return STV0900_INVALID_HANDLE; - if (stv0900_read_reg(i_params, R0900_MID) >= 0x20) { - stv0900_write_reg(i_params, R0900_TSGENERAL, 0x0c); - for (i = 0; i < 32; i++) - stv0900_write_reg(i_params, STV0900_Cut20_AddOnVal[i][0], STV0900_Cut20_AddOnVal[i][1]); - } + intp->chip_id = stv0900_read_reg(intp, R0900_MID); - stv0900_write_reg(i_params, R0900_P1_FSPYCFG, 0x6c); - stv0900_write_reg(i_params, R0900_P2_FSPYCFG, 0x6c); - stv0900_write_reg(i_params, R0900_TSTRES0, 0x80); - stv0900_write_reg(i_params, R0900_TSTRES0, 0x00); - } - error = i_params->errs; - } else - error = STV0900_INVALID_HANDLE; + if (intp->errs != STV0900_NO_ERROR) + return intp->errs; - return error; + /*Startup sequence*/ + stv0900_write_reg(intp, R0900_P1_DMDISTATE, 0x5c); + stv0900_write_reg(intp, R0900_P2_DMDISTATE, 0x5c); + msleep(3); + stv0900_write_reg(intp, R0900_P1_TNRCFG, 0x6c); + stv0900_write_reg(intp, R0900_P2_TNRCFG, 0x6f); + stv0900_write_reg(intp, R0900_P1_I2CRPT, 0x20); + stv0900_write_reg(intp, R0900_P2_I2CRPT, 0x20); + stv0900_write_reg(intp, R0900_NCOARSE, 0x13); + msleep(3); + stv0900_write_reg(intp, R0900_I2CCFG, 0x08); + switch (intp->clkmode) { + case 0: + case 2: + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 + | intp->clkmode); + break; + default: + /* preserve SELOSCI bit */ + i = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | i); + break; + } + + msleep(3); + for (i = 0; i < 181; i++) + stv0900_write_reg(intp, STV0900_InitVal[i][0], + STV0900_InitVal[i][1]); + + if (stv0900_read_reg(intp, R0900_MID) >= 0x20) { + stv0900_write_reg(intp, R0900_TSGENERAL, 0x0c); + for (i = 0; i < 32; i++) + stv0900_write_reg(intp, STV0900_Cut20_AddOnVal[i][0], + STV0900_Cut20_AddOnVal[i][1]); + } + + stv0900_write_reg(intp, R0900_P1_FSPYCFG, 0x6c); + stv0900_write_reg(intp, R0900_P2_FSPYCFG, 0x6c); + + stv0900_write_reg(intp, R0900_P1_PDELCTRL2, 0x01); + stv0900_write_reg(intp, R0900_P2_PDELCTRL2, 0x21); + + stv0900_write_reg(intp, R0900_P1_PDELCTRL3, 0x20); + stv0900_write_reg(intp, R0900_P2_PDELCTRL3, 0x20); + + stv0900_write_reg(intp, R0900_TSTRES0, 0x80); + stv0900_write_reg(intp, R0900_TSTRES0, 0x00); + + return STV0900_NO_ERROR; } -u32 stv0900_get_mclk_freq(struct stv0900_internal *i_params, u32 ext_clk) +u32 stv0900_get_mclk_freq(struct stv0900_internal *intp, u32 ext_clk) { u32 mclk = 90000000, div = 0, ad_div = 0; - div = stv0900_get_bits(i_params, F0900_M_DIV); - ad_div = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6); + div = stv0900_get_bits(intp, F0900_M_DIV); + ad_div = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); mclk = (div + 1) * ext_clk / ad_div; - dprintk(KERN_INFO "%s: Calculated Mclk = %d\n", __func__, mclk); + dprintk("%s: Calculated Mclk = %d\n", __func__, mclk); return mclk; } -enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *i_params, u32 mclk) +enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *intp, u32 mclk) { - enum fe_stv0900_error error = STV0900_NO_ERROR; u32 m_div, clk_sel; - dprintk(KERN_INFO "%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, - i_params->quartz); + dprintk("%s: Mclk set to %d, Quartz = %d\n", __func__, mclk, + intp->quartz); - if (i_params == NULL) - error = STV0900_INVALID_HANDLE; - else { - if (i_params->errs) - error = STV0900_I2C_ERROR; - else { - clk_sel = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6); - m_div = ((clk_sel * mclk) / i_params->quartz) - 1; - stv0900_write_bits(i_params, F0900_M_DIV, m_div); - i_params->mclk = stv0900_get_mclk_freq(i_params, - i_params->quartz); - - /*Set the DiseqC frequency to 22KHz */ - /* - Formula: - DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg) - DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg) - */ - m_div = i_params->mclk / 704000; - stv0900_write_reg(i_params, R0900_P1_F22TX, m_div); - stv0900_write_reg(i_params, R0900_P1_F22RX, m_div); - - stv0900_write_reg(i_params, R0900_P2_F22TX, m_div); - stv0900_write_reg(i_params, R0900_P2_F22RX, m_div); - - if ((i_params->errs)) - error = STV0900_I2C_ERROR; - } - } + if (intp == NULL) + return STV0900_INVALID_HANDLE; - return error; + if (intp->errs) + return STV0900_I2C_ERROR; + + clk_sel = ((stv0900_get_bits(intp, F0900_SELX1RATIO) == 1) ? 4 : 6); + m_div = ((clk_sel * mclk) / intp->quartz) - 1; + stv0900_write_bits(intp, F0900_M_DIV, m_div); + intp->mclk = stv0900_get_mclk_freq(intp, + intp->quartz); + + /*Set the DiseqC frequency to 22KHz */ + /* + Formula: + DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg) + DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg) + */ + m_div = intp->mclk / 704000; + stv0900_write_reg(intp, R0900_P1_F22TX, m_div); + stv0900_write_reg(intp, R0900_P1_F22RX, m_div); + + stv0900_write_reg(intp, R0900_P2_F22TX, m_div); + stv0900_write_reg(intp, R0900_P2_F22RX, m_div); + + if ((intp->errs)) + return STV0900_I2C_ERROR; + + return STV0900_NO_ERROR; } -u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr, +u32 stv0900_get_err_count(struct stv0900_internal *intp, int cntr, enum fe_stv0900_demod_num demod) { u32 lsb, msb, hsb, err_val; - s32 err1field_hsb, err1field_msb, err1field_lsb; - s32 err2field_hsb, err2field_msb, err2field_lsb; - - dmd_reg(err1field_hsb, F0900_P1_ERR_CNT12, F0900_P2_ERR_CNT12); - dmd_reg(err1field_msb, F0900_P1_ERR_CNT11, F0900_P2_ERR_CNT11); - dmd_reg(err1field_lsb, F0900_P1_ERR_CNT10, F0900_P2_ERR_CNT10); - - dmd_reg(err2field_hsb, F0900_P1_ERR_CNT22, F0900_P2_ERR_CNT22); - dmd_reg(err2field_msb, F0900_P1_ERR_CNT21, F0900_P2_ERR_CNT21); - dmd_reg(err2field_lsb, F0900_P1_ERR_CNT20, F0900_P2_ERR_CNT20); switch (cntr) { case 0: default: - hsb = stv0900_get_bits(i_params, err1field_hsb); - msb = stv0900_get_bits(i_params, err1field_msb); - lsb = stv0900_get_bits(i_params, err1field_lsb); + hsb = stv0900_get_bits(intp, ERR_CNT12); + msb = stv0900_get_bits(intp, ERR_CNT11); + lsb = stv0900_get_bits(intp, ERR_CNT10); break; case 1: - hsb = stv0900_get_bits(i_params, err2field_hsb); - msb = stv0900_get_bits(i_params, err2field_msb); - lsb = stv0900_get_bits(i_params, err2field_lsb); + hsb = stv0900_get_bits(intp, ERR_CNT22); + msb = stv0900_get_bits(intp, ERR_CNT21); + lsb = stv0900_get_bits(intp, ERR_CNT20); break; } @@ -364,26 +361,22 @@ u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr, static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - u32 fi2c; - - dmd_reg(fi2c, F0900_P1_I2CT_ON, F0900_P2_I2CT_ON); - - stv0900_write_bits(i_params, fi2c, enable); + stv0900_write_bits(intp, I2CT_ON, enable); return 0; } -static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, +static void stv0900_set_ts_parallel_serial(struct stv0900_internal *intp, enum fe_stv0900_clock_type path1_ts, enum fe_stv0900_clock_type path2_ts) { - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - if (i_params->chip_id >= 0x20) { + if (intp->chip_id >= 0x20) { switch (path1_ts) { case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: @@ -391,20 +384,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, case STV0900_SERIAL_PUNCT_CLOCK: case STV0900_SERIAL_CONT_CLOCK: default: - stv0900_write_reg(i_params, R0900_TSGENERAL, + stv0900_write_reg(intp, R0900_TSGENERAL, 0x00); break; case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: - stv0900_write_reg(i_params, R0900_TSGENERAL, + stv0900_write_reg(intp, R0900_TSGENERAL, 0x06); - stv0900_write_bits(i_params, + stv0900_write_bits(intp, F0900_P1_TSFIFO_MANSPEED, 3); - stv0900_write_bits(i_params, + stv0900_write_bits(intp, F0900_P2_TSFIFO_MANSPEED, 0); - stv0900_write_reg(i_params, + stv0900_write_reg(intp, R0900_P1_TSSPEED, 0x14); - stv0900_write_reg(i_params, + stv0900_write_reg(intp, R0900_P2_TSSPEED, 0x28); break; } @@ -416,14 +409,14 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, case STV0900_SERIAL_PUNCT_CLOCK: case STV0900_SERIAL_CONT_CLOCK: default: - stv0900_write_reg(i_params, + stv0900_write_reg(intp, R0900_TSGENERAL, 0x0C); break; case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: - stv0900_write_reg(i_params, + stv0900_write_reg(intp, R0900_TSGENERAL, 0x0A); - dprintk(KERN_INFO "%s: 0x0a\n", __func__); + dprintk("%s: 0x0a\n", __func__); break; } break; @@ -436,20 +429,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, case STV0900_SERIAL_PUNCT_CLOCK: case STV0900_SERIAL_CONT_CLOCK: default: - stv0900_write_reg(i_params, R0900_TSGENERAL1X, + stv0900_write_reg(intp, R0900_TSGENERAL1X, 0x10); break; case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: - stv0900_write_reg(i_params, R0900_TSGENERAL1X, + stv0900_write_reg(intp, R0900_TSGENERAL1X, 0x16); - stv0900_write_bits(i_params, + stv0900_write_bits(intp, F0900_P1_TSFIFO_MANSPEED, 3); - stv0900_write_bits(i_params, + stv0900_write_bits(intp, F0900_P2_TSFIFO_MANSPEED, 0); - stv0900_write_reg(i_params, R0900_P1_TSSPEED, + stv0900_write_reg(intp, R0900_P1_TSSPEED, 0x14); - stv0900_write_reg(i_params, R0900_P2_TSSPEED, + stv0900_write_reg(intp, R0900_P2_TSSPEED, 0x28); break; } @@ -462,14 +455,14 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, case STV0900_SERIAL_PUNCT_CLOCK: case STV0900_SERIAL_CONT_CLOCK: default: - stv0900_write_reg(i_params, R0900_TSGENERAL1X, + stv0900_write_reg(intp, R0900_TSGENERAL1X, 0x14); break; case STV0900_PARALLEL_PUNCT_CLOCK: case STV0900_DVBCI_CLOCK: - stv0900_write_reg(i_params, R0900_TSGENERAL1X, + stv0900_write_reg(intp, R0900_TSGENERAL1X, 0x12); - dprintk(KERN_INFO "%s: 0x12\n", __func__); + dprintk("%s: 0x12\n", __func__); break; } @@ -479,20 +472,20 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, switch (path1_ts) { case STV0900_PARALLEL_PUNCT_CLOCK: - stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00); - stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); break; case STV0900_DVBCI_CLOCK: - stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00); - stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); break; case STV0900_SERIAL_PUNCT_CLOCK: - stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01); - stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00); + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x00); break; case STV0900_SERIAL_CONT_CLOCK: - stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01); - stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P1_TSFIFO_DVBCI, 0x01); break; default: break; @@ -500,29 +493,29 @@ static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params, switch (path2_ts) { case STV0900_PARALLEL_PUNCT_CLOCK: - stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00); - stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); break; case STV0900_DVBCI_CLOCK: - stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00); - stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); break; case STV0900_SERIAL_PUNCT_CLOCK: - stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01); - stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00); + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x00); break; case STV0900_SERIAL_CONT_CLOCK: - stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01); - stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_SERIAL, 0x01); + stv0900_write_bits(intp, F0900_P2_TSFIFO_DVBCI, 0x01); break; default: break; } - stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1); - stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 0); - stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1); - stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 0); + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); } void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency, @@ -574,7 +567,7 @@ void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth) } } -static s32 stv0900_get_rf_level(struct stv0900_internal *i_params, +static s32 stv0900_get_rf_level(struct stv0900_internal *intp, const struct stv0900_table *lookup, enum fe_stv0900_demod_num demod) { @@ -584,45 +577,41 @@ static s32 stv0900_get_rf_level(struct stv0900_internal *i_params, i, rf_lvl = 0; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - if ((lookup != NULL) && lookup->size) { - switch (demod) { - case STV0900_DEMOD_1: - default: - agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE1), - stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE0)); - break; - case STV0900_DEMOD_2: - agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE1), - stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE0)); - break; - } + if ((lookup == NULL) || (lookup->size <= 0)) + return 0; - imin = 0; - imax = lookup->size - 1; - if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[imax].regval)) { - while ((imax - imin) > 1) { - i = (imax + imin) >> 1; + agc_gain = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), + stv0900_get_bits(intp, AGCIQ_VALUE0)); - if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[i].regval)) - imax = i; - else - imin = i; - } + imin = 0; + imax = lookup->size - 1; + if (INRANGE(lookup->table[imin].regval, agc_gain, + lookup->table[imax].regval)) { + while ((imax - imin) > 1) { + i = (imax + imin) >> 1; - rf_lvl = (((s32)agc_gain - lookup->table[imin].regval) - * (lookup->table[imax].realval - lookup->table[imin].realval) - / (lookup->table[imax].regval - lookup->table[imin].regval)) - + lookup->table[imin].realval; - } else if (agc_gain > lookup->table[0].regval) - rf_lvl = 5; - else if (agc_gain < lookup->table[lookup->size-1].regval) - rf_lvl = -100; + if (INRANGE(lookup->table[imin].regval, + agc_gain, + lookup->table[i].regval)) + imax = i; + else + imin = i; + } - } + rf_lvl = (s32)agc_gain - lookup->table[imin].regval; + rf_lvl *= (lookup->table[imax].realval - + lookup->table[imin].realval); + rf_lvl /= (lookup->table[imax].regval - + lookup->table[imin].regval); + rf_lvl += lookup->table[imin].realval; + } else if (agc_gain > lookup->table[0].regval) + rf_lvl = 5; + else if (agc_gain < lookup->table[lookup->size-1].regval) + rf_lvl = -100; - dprintk(KERN_INFO "%s: RFLevel = %d\n", __func__, rf_lvl); + dprintk("%s: RFLevel = %d\n", __func__, rf_lvl); return rf_lvl; } @@ -634,50 +623,51 @@ static int stv0900_read_signal_strength(struct dvb_frontend *fe, u16 *strength) s32 rflevel = stv0900_get_rf_level(internal, &stv0900_rf, state->demod); - *strength = (rflevel + 100) * (16383 / 105); + rflevel = (rflevel + 100) * (65535 / 70); + if (rflevel < 0) + rflevel = 0; + + if (rflevel > 65535) + rflevel = 65535; + + *strength = rflevel; return 0; } - static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, const struct stv0900_table *lookup) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 c_n = -100, - regval, imin, imax, + s32 c_n = -100, + regval, + imin, + imax, i, - lock_flag_field, noise_field1, noise_field0; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - dmd_reg(lock_flag_field, F0900_P1_LOCK_DEFINITIF, - F0900_P2_LOCK_DEFINITIF); if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) { - dmd_reg(noise_field1, F0900_P1_NOSPLHT_NORMED1, - F0900_P2_NOSPLHT_NORMED1); - dmd_reg(noise_field0, F0900_P1_NOSPLHT_NORMED0, - F0900_P2_NOSPLHT_NORMED0); + noise_field1 = NOSPLHT_NORMED1; + noise_field0 = NOSPLHT_NORMED0; } else { - dmd_reg(noise_field1, F0900_P1_NOSDATAT_NORMED1, - F0900_P2_NOSDATAT_NORMED1); - dmd_reg(noise_field0, F0900_P1_NOSDATAT_NORMED0, - F0900_P2_NOSDATAT_NORMED0); + noise_field1 = NOSDATAT_NORMED1; + noise_field0 = NOSDATAT_NORMED0; } - if (stv0900_get_bits(i_params, lock_flag_field)) { + if (stv0900_get_bits(intp, LOCK_DEFINITIF)) { if ((lookup != NULL) && lookup->size) { regval = 0; msleep(5); for (i = 0; i < 16; i++) { - regval += MAKEWORD(stv0900_get_bits(i_params, + regval += MAKEWORD(stv0900_get_bits(intp, noise_field1), - stv0900_get_bits(i_params, + stv0900_get_bits(intp, noise_field0)); msleep(1); } @@ -715,10 +705,9 @@ static s32 stv0900_carr_get_quality(struct dvb_frontend *fe, static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; u8 err_val1, err_val0; - s32 err_field1, err_field0; u32 header_err_val = 0; *ucblocks = 0x0; @@ -726,24 +715,14 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) /* DVB-S2 delineator errors count */ /* retreiving number for errnous headers */ - dmd_reg(err_field0, R0900_P1_BBFCRCKO0, - R0900_P2_BBFCRCKO0); - dmd_reg(err_field1, R0900_P1_BBFCRCKO1, - R0900_P2_BBFCRCKO1); - - err_val1 = stv0900_read_reg(i_params, err_field1); - err_val0 = stv0900_read_reg(i_params, err_field0); - header_err_val = (err_val1<<8) | err_val0; + err_val1 = stv0900_read_reg(intp, BBFCRCKO1); + err_val0 = stv0900_read_reg(intp, BBFCRCKO0); + header_err_val = (err_val1 << 8) | err_val0; /* retreiving number for errnous packets */ - dmd_reg(err_field0, R0900_P1_UPCRCKO0, - R0900_P2_UPCRCKO0); - dmd_reg(err_field1, R0900_P1_UPCRCKO1, - R0900_P2_UPCRCKO1); - - err_val1 = stv0900_read_reg(i_params, err_field1); - err_val0 = stv0900_read_reg(i_params, err_field0); - *ucblocks = (err_val1<<8) | err_val0; + err_val1 = stv0900_read_reg(intp, UPCRCKO1); + err_val0 = stv0900_read_reg(intp, UPCRCKO0); + *ucblocks = (err_val1 << 8) | err_val0; *ucblocks += header_err_val; } @@ -752,33 +731,27 @@ static int stv0900_read_ucblocks(struct dvb_frontend *fe, u32 * ucblocks) static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr) { - *snr = stv0900_carr_get_quality(fe, + s32 snrlcl = stv0900_carr_get_quality(fe, (const struct stv0900_table *)&stv0900_s2_cn); - *snr += 30; - *snr *= (16383 / 1030); + snrlcl = (snrlcl + 30) * 384; + if (snrlcl < 0) + snrlcl = 0; + + if (snrlcl > 65535) + snrlcl = 65535; + + *snr = snrlcl; return 0; } -static u32 stv0900_get_ber(struct stv0900_internal *i_params, +static u32 stv0900_get_ber(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { u32 ber = 10000000, i; - s32 dmd_state_reg; s32 demod_state; - s32 vstatus_reg; - s32 prvit_field; - s32 pdel_status_reg; - s32 pdel_lock_field; - - dmd_reg(dmd_state_reg, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(vstatus_reg, R0900_P1_VSTATUSVIT, R0900_P2_VSTATUSVIT); - dmd_reg(prvit_field, F0900_P1_PRFVIT, F0900_P2_PRFVIT); - dmd_reg(pdel_status_reg, R0900_P1_PDELSTATUS1, R0900_P2_PDELSTATUS1); - dmd_reg(pdel_lock_field, F0900_P1_PKTDELIN_LOCK, - F0900_P2_PKTDELIN_LOCK); - demod_state = stv0900_get_bits(i_params, dmd_state_reg); + demod_state = stv0900_get_bits(intp, HEADER_MODE); switch (demod_state) { case STV0900_SEARCH: @@ -790,11 +763,11 @@ static u32 stv0900_get_ber(struct stv0900_internal *i_params, ber = 0; for (i = 0; i < 5; i++) { msleep(5); - ber += stv0900_get_err_count(i_params, 0, demod); + ber += stv0900_get_err_count(intp, 0, demod); } ber /= 5; - if (stv0900_get_bits(i_params, prvit_field)) { + if (stv0900_get_bits(intp, PRFVIT)) { ber *= 9766; ber = ber >> 13; } @@ -804,11 +777,11 @@ static u32 stv0900_get_ber(struct stv0900_internal *i_params, ber = 0; for (i = 0; i < 5; i++) { msleep(5); - ber += stv0900_get_err_count(i_params, 0, demod); + ber += stv0900_get_err_count(intp, 0, demod); } ber /= 5; - if (stv0900_get_bits(i_params, pdel_lock_field)) { + if (stv0900_get_bits(intp, PKTDELIN_LOCK)) { ber *= 9766; ber = ber >> 13; } @@ -829,20 +802,16 @@ static int stv0900_read_ber(struct dvb_frontend *fe, u32 *ber) return 0; } -int stv0900_get_demod_lock(struct stv0900_internal *i_params, +int stv0900_get_demod_lock(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod, s32 time_out) { s32 timer = 0, - lock = 0, - header_field, - lock_field; + lock = 0; enum fe_stv0900_search_state dmd_state; - dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF); while ((timer < time_out) && (lock == 0)) { - dmd_state = stv0900_get_bits(i_params, header_field); + dmd_state = stv0900_get_bits(intp, HEADER_MODE); dprintk("Demod State = %d\n", dmd_state); switch (dmd_state) { case STV0900_SEARCH: @@ -852,7 +821,7 @@ int stv0900_get_demod_lock(struct stv0900_internal *i_params, break; case STV0900_DVBS2_FOUND: case STV0900_DVBS_FOUND: - lock = stv0900_get_bits(i_params, lock_field); + lock = stv0900_get_bits(intp, LOCK_DEFINITIF); break; } @@ -870,56 +839,40 @@ int stv0900_get_demod_lock(struct stv0900_internal *i_params, return lock; } -void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params, +void stv0900_stop_all_s2_modcod(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { s32 regflist, i; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - dmd_reg(regflist, R0900_P1_MODCODLST0, R0900_P2_MODCODLST0); + regflist = MODCODLST0; for (i = 0; i < 16; i++) - stv0900_write_reg(i_params, regflist + i, 0xff); + stv0900_write_reg(intp, regflist + i, 0xff); } -void stv0900_activate_s2_modcode(struct stv0900_internal *i_params, +void stv0900_activate_s2_modcod(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { u32 matype, - mod_code, - fmod, - reg_index, - field_index; + mod_code, + fmod, + reg_index, + field_index; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - if (i_params->chip_id <= 0x11) { + if (intp->chip_id <= 0x11) { msleep(5); - switch (demod) { - case STV0900_DEMOD_1: - default: - mod_code = stv0900_read_reg(i_params, - R0900_P1_PLHMODCOD); - matype = mod_code & 0x3; - mod_code = (mod_code & 0x7f) >> 2; - - reg_index = R0900_P1_MODCODLSTF - mod_code / 2; - field_index = mod_code % 2; - break; - case STV0900_DEMOD_2: - mod_code = stv0900_read_reg(i_params, - R0900_P2_PLHMODCOD); - matype = mod_code & 0x3; - mod_code = (mod_code & 0x7f) >> 2; - - reg_index = R0900_P2_MODCODLSTF - mod_code / 2; - field_index = mod_code % 2; - break; - } + mod_code = stv0900_read_reg(intp, PLHMODCOD); + matype = mod_code & 0x3; + mod_code = (mod_code & 0x7f) >> 2; + reg_index = MODCODLSTF - mod_code / 2; + field_index = mod_code % 2; switch (matype) { case 0: @@ -938,70 +891,41 @@ void stv0900_activate_s2_modcode(struct stv0900_internal *i_params, } if ((INRANGE(STV0900_QPSK_12, mod_code, STV0900_8PSK_910)) - && (matype <= 1)) { + && (matype <= 1)) { if (field_index == 0) - stv0900_write_reg(i_params, reg_index, + stv0900_write_reg(intp, reg_index, 0xf0 | fmod); else - stv0900_write_reg(i_params, reg_index, + stv0900_write_reg(intp, reg_index, (fmod << 4) | 0xf); } - } else if (i_params->chip_id >= 0x12) { - switch (demod) { - case STV0900_DEMOD_1: - default: - for (reg_index = 0; reg_index < 7; reg_index++) - stv0900_write_reg(i_params, R0900_P1_MODCODLST0 + reg_index, 0xff); - stv0900_write_reg(i_params, R0900_P1_MODCODLSTE, 0xff); - stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0xcf); - for (reg_index = 0; reg_index < 8; reg_index++) - stv0900_write_reg(i_params, R0900_P1_MODCODLST7 + reg_index, 0xcc); + } else if (intp->chip_id >= 0x12) { + for (reg_index = 0; reg_index < 7; reg_index++) + stv0900_write_reg(intp, MODCODLST0 + reg_index, 0xff); - break; - case STV0900_DEMOD_2: - for (reg_index = 0; reg_index < 7; reg_index++) - stv0900_write_reg(i_params, R0900_P2_MODCODLST0 + reg_index, 0xff); + stv0900_write_reg(intp, MODCODLSTE, 0xff); + stv0900_write_reg(intp, MODCODLSTF, 0xcf); + for (reg_index = 0; reg_index < 8; reg_index++) + stv0900_write_reg(intp, MODCODLST7 + reg_index, 0xcc); - stv0900_write_reg(i_params, R0900_P2_MODCODLSTE, 0xff); - stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0xcf); - for (reg_index = 0; reg_index < 8; reg_index++) - stv0900_write_reg(i_params, R0900_P2_MODCODLST7 + reg_index, 0xcc); - - break; - } } } -void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params, +void stv0900_activate_s2_modcod_single(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { u32 reg_index; - dprintk(KERN_INFO "%s\n", __func__); - - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_reg(i_params, R0900_P1_MODCODLST0, 0xff); - stv0900_write_reg(i_params, R0900_P1_MODCODLST1, 0xf0); - stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0x0f); - for (reg_index = 0; reg_index < 13; reg_index++) - stv0900_write_reg(i_params, - R0900_P1_MODCODLST2 + reg_index, 0); + dprintk("%s\n", __func__); - break; - case STV0900_DEMOD_2: - stv0900_write_reg(i_params, R0900_P2_MODCODLST0, 0xff); - stv0900_write_reg(i_params, R0900_P2_MODCODLST1, 0xf0); - stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0x0f); - for (reg_index = 0; reg_index < 13; reg_index++) - stv0900_write_reg(i_params, - R0900_P2_MODCODLST2 + reg_index, 0); + stv0900_write_reg(intp, MODCODLST0, 0xff); + stv0900_write_reg(intp, MODCODLST1, 0xf0); + stv0900_write_reg(intp, MODCODLSTF, 0x0f); + for (reg_index = 0; reg_index < 13; reg_index++) + stv0900_write_reg(intp, MODCODLST2 + reg_index, 0); - break; - } } static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe) @@ -1012,7 +936,7 @@ static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe) static int stb0900_set_property(struct dvb_frontend *fe, struct dtv_property *tvp) { - dprintk(KERN_INFO "%s(..)\n", __func__); + dprintk("%s(..)\n", __func__); return 0; } @@ -1020,166 +944,123 @@ static int stb0900_set_property(struct dvb_frontend *fe, static int stb0900_get_property(struct dvb_frontend *fe, struct dtv_property *tvp) { - dprintk(KERN_INFO "%s(..)\n", __func__); + dprintk("%s(..)\n", __func__); return 0; } -void stv0900_start_search(struct stv0900_internal *i_params, +void stv0900_start_search(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1f); - - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xaa); - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55); - - if (i_params->dmd1_symbol_rate <= 5000000) { - stv0900_write_reg(i_params, R0900_P1_CARCFG, 0x44); - stv0900_write_reg(i_params, R0900_P1_CFRUP1, 0x0f); - stv0900_write_reg(i_params, R0900_P1_CFRUP0, 0xff); - stv0900_write_reg(i_params, R0900_P1_CFRLOW1, 0xf0); - stv0900_write_reg(i_params, R0900_P1_CFRLOW0, 0x00); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68); + u32 freq; + s16 freq_s16 ; + + stv0900_write_bits(intp, DEMOD_MODE, 0x1f); + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0xaa); + + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x55); + + if (intp->chip_id <= 0x20) { + if (intp->symbol_rate[0] <= 5000000) { + stv0900_write_reg(intp, CARCFG, 0x44); + stv0900_write_reg(intp, CFRUP1, 0x0f); + stv0900_write_reg(intp, CFRUP0, 0xff); + stv0900_write_reg(intp, CFRLOW1, 0xf0); + stv0900_write_reg(intp, CFRLOW0, 0x00); + stv0900_write_reg(intp, RTCS2, 0x68); } else { - stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xc4); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44); - } - - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0); - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41); - stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41); - - if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) { - stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82); - stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0); - } + stv0900_write_reg(intp, CARCFG, 0xc4); + stv0900_write_reg(intp, RTCS2, 0x44); } - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xe0); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xc0); - stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - stv0900_write_bits(i_params, F0900_P1_S1S2_SEQUENTIAL, 0); - stv0900_write_reg(i_params, R0900_P1_RTC, 0x88); - if (i_params->chip_id >= 0x20) { - if (i_params->dmd1_symbol_rate < 2000000) { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x39); - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x40); - } - - if (i_params->dmd1_symbol_rate < 10000000) { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4c); - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20); - } else { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4b); - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20); - } + } else { /*cut 3.0 above*/ + if (intp->symbol_rate[demod] <= 5000000) + stv0900_write_reg(intp, RTCS2, 0x68); + else + stv0900_write_reg(intp, RTCS2, 0x44); + stv0900_write_reg(intp, CARCFG, 0x46); + if (intp->srch_algo[demod] == STV0900_WARM_START) { + freq = 1000 << 16; + freq /= (intp->mclk / 1000); + freq_s16 = (s16)freq; } else { - if (i_params->dmd1_symbol_rate < 10000000) - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xef); + freq = (intp->srch_range[demod] / 2000); + if (intp->symbol_rate[demod] <= 5000000) + freq += 80; else - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed); - } + freq += 600; - switch (i_params->dmd1_srch_algo) { - case STV0900_WARM_START: - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - break; - case STV0900_COLD_START: - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15); - break; - default: - break; - } - - break; - case STV0900_DEMOD_2: - stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1f); - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xaa); - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55); - - if (i_params->dmd2_symbol_rate <= 5000000) { - stv0900_write_reg(i_params, R0900_P2_CARCFG, 0x44); - stv0900_write_reg(i_params, R0900_P2_CFRUP1, 0x0f); - stv0900_write_reg(i_params, R0900_P2_CFRUP0, 0xff); - stv0900_write_reg(i_params, R0900_P2_CFRLOW1, 0xf0); - stv0900_write_reg(i_params, R0900_P2_CFRLOW0, 0x00); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68); - } else { - stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xc4); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44); + freq = freq << 16; + freq /= (intp->mclk / 1000); + freq_s16 = (s16)freq; } - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0); + stv0900_write_bits(intp, CFR_UP1, MSB(freq_s16)); + stv0900_write_bits(intp, CFR_UP0, LSB(freq_s16)); + freq_s16 *= (-1); + stv0900_write_bits(intp, CFR_LOW1, MSB(freq_s16)); + stv0900_write_bits(intp, CFR_LOW0, LSB(freq_s16)); + } - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41); - stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41); - if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) { - stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82); - stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0); - } - } + stv0900_write_reg(intp, CFRINIT1, 0); + stv0900_write_reg(intp, CFRINIT0, 0); - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xe0); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xc0); - stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - stv0900_write_bits(i_params, F0900_P2_S1S2_SEQUENTIAL, 0); - stv0900_write_reg(i_params, R0900_P2_RTC, 0x88); - if (i_params->chip_id >= 0x20) { - if (i_params->dmd2_symbol_rate < 2000000) { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x39); - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x40); - } + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, EQUALCFG, 0x41); + stv0900_write_reg(intp, FFECFG, 0x41); - if (i_params->dmd2_symbol_rate < 10000000) { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4c); - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20); - } else { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4b); - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20); - } + if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || + (intp->srch_standard[demod] == STV0900_SEARCH_DSS) || + (intp->srch_standard[demod] == STV0900_AUTO_SEARCH)) { + stv0900_write_reg(intp, VITSCALE, + 0x82); + stv0900_write_reg(intp, VAVSRVIT, 0x0); + } + } + stv0900_write_reg(intp, SFRSTEP, 0x00); + stv0900_write_reg(intp, TMGTHRISE, 0xe0); + stv0900_write_reg(intp, TMGTHFALL, 0xc0); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_bits(intp, S1S2_SEQUENTIAL, 0); + stv0900_write_reg(intp, RTC, 0x88); + if (intp->chip_id >= 0x20) { + if (intp->symbol_rate[demod] < 2000000) { + if (intp->chip_id <= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x39); + else /*cut 3.0*/ + stv0900_write_reg(intp, CARFREQ, 0x89); + + stv0900_write_reg(intp, CARHDR, 0x40); + } else if (intp->symbol_rate[demod] < 10000000) { + stv0900_write_reg(intp, CARFREQ, 0x4c); + stv0900_write_reg(intp, CARHDR, 0x20); } else { - if (i_params->dmd2_symbol_rate < 10000000) - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xef); - else - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed); + stv0900_write_reg(intp, CARFREQ, 0x4b); + stv0900_write_reg(intp, CARHDR, 0x20); } - switch (i_params->dmd2_srch_algo) { - case STV0900_WARM_START: - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - break; - case STV0900_COLD_START: - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15); - break; - default: - break; - } + } else { + if (intp->symbol_rate[demod] < 10000000) + stv0900_write_reg(intp, CARFREQ, 0xef); + else + stv0900_write_reg(intp, CARFREQ, 0xed); + } + switch (intp->srch_algo[demod]) { + case STV0900_WARM_START: + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x18); + break; + case STV0900_COLD_START: + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + break; + default: break; } } @@ -1188,33 +1069,40 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, s32 pilot, u8 chip_id) { u8 aclc_value = 0x29; - s32 i; - const struct stv0900_car_loop_optim *car_loop_s2; - - dprintk(KERN_INFO "%s\n", __func__); - - if (chip_id <= 0x12) - car_loop_s2 = FE_STV0900_S2CarLoop; - else if (chip_id == 0x20) - car_loop_s2 = FE_STV0900_S2CarLoopCut20; - else - car_loop_s2 = FE_STV0900_S2CarLoop; + s32 i; + const struct stv0900_car_loop_optim *cls2, *cllqs2, *cllas2; + + dprintk("%s\n", __func__); + + if (chip_id <= 0x12) { + cls2 = FE_STV0900_S2CarLoop; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; + cllas2 = FE_STV0900_S2APSKCarLoopCut30; + } else if (chip_id == 0x20) { + cls2 = FE_STV0900_S2CarLoopCut20; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut20; + cllas2 = FE_STV0900_S2APSKCarLoopCut20; + } else { + cls2 = FE_STV0900_S2CarLoopCut30; + cllqs2 = FE_STV0900_S2LowQPCarLoopCut30; + cllas2 = FE_STV0900_S2APSKCarLoopCut30; + } if (modcode < STV0900_QPSK_12) { i = 0; - while ((i < 3) && (modcode != FE_STV0900_S2LowQPCarLoopCut20[i].modcode)) + while ((i < 3) && (modcode != cllqs2[i].modcode)) i++; if (i >= 3) i = 2; } else { i = 0; - while ((i < 14) && (modcode != car_loop_s2[i].modcode)) + while ((i < 14) && (modcode != cls2[i].modcode)) i++; if (i >= 14) { i = 0; - while ((i < 11) && (modcode != FE_STV0900_S2APSKCarLoopCut20[i].modcode)) + while ((i < 11) && (modcode != cllas2[i].modcode)) i++; if (i >= 11) @@ -1225,76 +1113,82 @@ u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode, if (modcode <= STV0900_QPSK_25) { if (pilot) { if (srate <= 3000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_2; + aclc_value = cllqs2[i].car_loop_pilots_on_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_5; + aclc_value = cllqs2[i].car_loop_pilots_on_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_10; + aclc_value = cllqs2[i].car_loop_pilots_on_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_20; + aclc_value = cllqs2[i].car_loop_pilots_on_20; else - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_30; + aclc_value = cllqs2[i].car_loop_pilots_on_30; } else { if (srate <= 3000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_2; + aclc_value = cllqs2[i].car_loop_pilots_off_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_5; + aclc_value = cllqs2[i].car_loop_pilots_off_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_10; + aclc_value = cllqs2[i].car_loop_pilots_off_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_20; + aclc_value = cllqs2[i].car_loop_pilots_off_20; else - aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_30; + aclc_value = cllqs2[i].car_loop_pilots_off_30; } } else if (modcode <= STV0900_8PSK_910) { if (pilot) { if (srate <= 3000000) - aclc_value = car_loop_s2[i].car_loop_pilots_on_2; + aclc_value = cls2[i].car_loop_pilots_on_2; else if (srate <= 7000000) - aclc_value = car_loop_s2[i].car_loop_pilots_on_5; + aclc_value = cls2[i].car_loop_pilots_on_5; else if (srate <= 15000000) - aclc_value = car_loop_s2[i].car_loop_pilots_on_10; + aclc_value = cls2[i].car_loop_pilots_on_10; else if (srate <= 25000000) - aclc_value = car_loop_s2[i].car_loop_pilots_on_20; + aclc_value = cls2[i].car_loop_pilots_on_20; else - aclc_value = car_loop_s2[i].car_loop_pilots_on_30; + aclc_value = cls2[i].car_loop_pilots_on_30; } else { if (srate <= 3000000) - aclc_value = car_loop_s2[i].car_loop_pilots_off_2; + aclc_value = cls2[i].car_loop_pilots_off_2; else if (srate <= 7000000) - aclc_value = car_loop_s2[i].car_loop_pilots_off_5; + aclc_value = cls2[i].car_loop_pilots_off_5; else if (srate <= 15000000) - aclc_value = car_loop_s2[i].car_loop_pilots_off_10; + aclc_value = cls2[i].car_loop_pilots_off_10; else if (srate <= 25000000) - aclc_value = car_loop_s2[i].car_loop_pilots_off_20; + aclc_value = cls2[i].car_loop_pilots_off_20; else - aclc_value = car_loop_s2[i].car_loop_pilots_off_30; + aclc_value = cls2[i].car_loop_pilots_off_30; } } else { if (srate <= 3000000) - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_2; + aclc_value = cllas2[i].car_loop_pilots_on_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_5; + aclc_value = cllas2[i].car_loop_pilots_on_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_10; + aclc_value = cllas2[i].car_loop_pilots_on_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_20; + aclc_value = cllas2[i].car_loop_pilots_on_20; else - aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_30; + aclc_value = cllas2[i].car_loop_pilots_on_30; } return aclc_value; } -u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modulation, u8 chip_id) +u8 stv0900_get_optim_short_carr_loop(s32 srate, + enum fe_stv0900_modulation modulation, + u8 chip_id) { + const struct stv0900_short_frames_car_loop_optim *s2scl; + const struct stv0900_short_frames_car_loop_optim_vs_mod *s2sclc30; s32 mod_index = 0; - u8 aclc_value = 0x0b; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); + + s2scl = FE_STV0900_S2ShortCarLoop; + s2sclc30 = FE_STV0900_S2ShortCarLoopCut30; switch (modulation) { case STV0900_QPSK: @@ -1312,75 +1206,116 @@ u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modul break; } - switch (chip_id) { - case 0x20: + if (chip_id >= 0x30) { if (srate <= 3000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_2; + aclc_value = s2sclc30[mod_index].car_loop_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_5; + aclc_value = s2sclc30[mod_index].car_loop_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_10; + aclc_value = s2sclc30[mod_index].car_loop_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_20; + aclc_value = s2sclc30[mod_index].car_loop_20; else - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_30; + aclc_value = s2sclc30[mod_index].car_loop_30; - break; - case 0x12: - default: + } else if (chip_id >= 0x20) { if (srate <= 3000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_2; + aclc_value = s2scl[mod_index].car_loop_cut20_2; else if (srate <= 7000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_5; + aclc_value = s2scl[mod_index].car_loop_cut20_5; else if (srate <= 15000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_10; + aclc_value = s2scl[mod_index].car_loop_cut20_10; else if (srate <= 25000000) - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_20; + aclc_value = s2scl[mod_index].car_loop_cut20_20; else - aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_30; + aclc_value = s2scl[mod_index].car_loop_cut20_30; + + } else { + if (srate <= 3000000) + aclc_value = s2scl[mod_index].car_loop_cut12_2; + else if (srate <= 7000000) + aclc_value = s2scl[mod_index].car_loop_cut12_5; + else if (srate <= 15000000) + aclc_value = s2scl[mod_index].car_loop_cut12_10; + else if (srate <= 25000000) + aclc_value = s2scl[mod_index].car_loop_cut12_20; + else + aclc_value = s2scl[mod_index].car_loop_cut12_30; - break; } return aclc_value; } -static enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *i_params, +static +enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *intp, enum fe_stv0900_demod_mode LDPC_Mode, enum fe_stv0900_demod_num demod) { enum fe_stv0900_error error = STV0900_NO_ERROR; + s32 reg_ind; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); switch (LDPC_Mode) { case STV0900_DUAL: default: - if ((i_params->demod_mode != STV0900_DUAL) - || (stv0900_get_bits(i_params, F0900_DDEMOD) != 1)) { - stv0900_write_reg(i_params, R0900_GENCFG, 0x1d); - - i_params->demod_mode = STV0900_DUAL; - - stv0900_write_bits(i_params, F0900_FRESFEC, 1); - stv0900_write_bits(i_params, F0900_FRESFEC, 0); + if ((intp->demod_mode != STV0900_DUAL) + || (stv0900_get_bits(intp, F0900_DDEMOD) != 1)) { + stv0900_write_reg(intp, R0900_GENCFG, 0x1d); + + intp->demod_mode = STV0900_DUAL; + + stv0900_write_bits(intp, F0900_FRESFEC, 1); + stv0900_write_bits(intp, F0900_FRESFEC, 0); + + for (reg_ind = 0; reg_ind < 7; reg_ind++) + stv0900_write_reg(intp, + R0900_P1_MODCODLST0 + reg_ind, + 0xff); + for (reg_ind = 0; reg_ind < 8; reg_ind++) + stv0900_write_reg(intp, + R0900_P1_MODCODLST7 + reg_ind, + 0xcc); + + stv0900_write_reg(intp, R0900_P1_MODCODLSTE, 0xff); + stv0900_write_reg(intp, R0900_P1_MODCODLSTF, 0xcf); + + for (reg_ind = 0; reg_ind < 7; reg_ind++) + stv0900_write_reg(intp, + R0900_P2_MODCODLST0 + reg_ind, + 0xff); + for (reg_ind = 0; reg_ind < 8; reg_ind++) + stv0900_write_reg(intp, + R0900_P2_MODCODLST7 + reg_ind, + 0xcc); + + stv0900_write_reg(intp, R0900_P2_MODCODLSTE, 0xff); + stv0900_write_reg(intp, R0900_P2_MODCODLSTF, 0xcf); } break; case STV0900_SINGLE: - if (demod == STV0900_DEMOD_2) - stv0900_write_reg(i_params, R0900_GENCFG, 0x06); - else - stv0900_write_reg(i_params, R0900_GENCFG, 0x04); + if (demod == STV0900_DEMOD_2) { + stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_1); + stv0900_activate_s2_modcod_single(intp, + STV0900_DEMOD_2); + stv0900_write_reg(intp, R0900_GENCFG, 0x06); + } else { + stv0900_stop_all_s2_modcod(intp, STV0900_DEMOD_2); + stv0900_activate_s2_modcod_single(intp, + STV0900_DEMOD_1); + stv0900_write_reg(intp, R0900_GENCFG, 0x04); + } - i_params->demod_mode = STV0900_SINGLE; + intp->demod_mode = STV0900_SINGLE; - stv0900_write_bits(i_params, F0900_FRESFEC, 1); - stv0900_write_bits(i_params, F0900_FRESFEC, 0); - stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1); - stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0); - stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1); - stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0); + stv0900_write_bits(intp, F0900_FRESFEC, 1); + stv0900_write_bits(intp, F0900_FRESFEC, 0); + stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 1); + stv0900_write_bits(intp, F0900_P1_ALGOSWRST, 0); + stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 1); + stv0900_write_bits(intp, F0900_P2_ALGOSWRST, 0); break; } @@ -1393,131 +1328,131 @@ static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe, struct stv0900_state *state = fe->demodulator_priv; enum fe_stv0900_error error = STV0900_NO_ERROR; enum fe_stv0900_error demodError = STV0900_NO_ERROR; + struct stv0900_internal *intp = NULL; + int selosci, i; struct stv0900_inode *temp_int = find_inode(state->i2c_adap, state->config->demod_address); - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - if (temp_int != NULL) { + if ((temp_int != NULL) && (p_init->demod_mode == STV0900_DUAL)) { state->internal = temp_int->internal; (state->internal->dmds_used)++; - dprintk(KERN_INFO "%s: Find Internal Structure!\n", __func__); + dprintk("%s: Find Internal Structure!\n", __func__); return STV0900_NO_ERROR; } else { - state->internal = kmalloc(sizeof(struct stv0900_internal), GFP_KERNEL); + state->internal = kmalloc(sizeof(struct stv0900_internal), + GFP_KERNEL); temp_int = append_internal(state->internal); state->internal->dmds_used = 1; state->internal->i2c_adap = state->i2c_adap; state->internal->i2c_addr = state->config->demod_address; state->internal->clkmode = state->config->clkmode; state->internal->errs = STV0900_NO_ERROR; - dprintk(KERN_INFO "%s: Create New Internal Structure!\n", __func__); + dprintk("%s: Create New Internal Structure!\n", __func__); } - if (state->internal != NULL) { - demodError = stv0900_initialize(state->internal); - if (demodError == STV0900_NO_ERROR) { - error = STV0900_NO_ERROR; - } else { - if (demodError == STV0900_INVALID_HANDLE) - error = STV0900_INVALID_HANDLE; - else - error = STV0900_I2C_ERROR; - } + if (state->internal == NULL) { + error = STV0900_INVALID_HANDLE; + return error; + } - if (state->internal != NULL) { - if (error == STV0900_NO_ERROR) { - state->internal->demod_mode = p_init->demod_mode; - - stv0900_st_dvbs2_single(state->internal, state->internal->demod_mode, STV0900_DEMOD_1); - - state->internal->chip_id = stv0900_read_reg(state->internal, R0900_MID); - state->internal->rolloff = p_init->rolloff; - state->internal->quartz = p_init->dmd_ref_clk; - - stv0900_write_bits(state->internal, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); - stv0900_write_bits(state->internal, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); - - state->internal->ts_config = p_init->ts_config; - if (state->internal->ts_config == NULL) - stv0900_set_ts_parallel_serial(state->internal, - p_init->path1_ts_clock, - p_init->path2_ts_clock); - else { - for (i = 0; state->internal->ts_config[i].addr != 0xffff; i++) - stv0900_write_reg(state->internal, - state->internal->ts_config[i].addr, - state->internal->ts_config[i].val); - - stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 1); - stv0900_write_bits(state->internal, F0900_P2_RST_HWARE, 0); - stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 1); - stv0900_write_bits(state->internal, F0900_P1_RST_HWARE, 0); - } + demodError = stv0900_initialize(state->internal); + if (demodError == STV0900_NO_ERROR) { + error = STV0900_NO_ERROR; + } else { + if (demodError == STV0900_INVALID_HANDLE) + error = STV0900_INVALID_HANDLE; + else + error = STV0900_I2C_ERROR; - stv0900_write_bits(state->internal, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); - switch (p_init->tuner1_adc) { - case 1: - stv0900_write_reg(state->internal, R0900_TSTTNR1, 0x26); - break; - default: - break; - } + return error; + } - stv0900_write_bits(state->internal, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress); - switch (p_init->tuner2_adc) { - case 1: - stv0900_write_reg(state->internal, R0900_TSTTNR3, 0x26); - break; - default: - break; - } + if (state->internal == NULL) { + error = STV0900_INVALID_HANDLE; + return error; + } - stv0900_write_bits(state->internal, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inversion); - stv0900_write_bits(state->internal, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inversion); - stv0900_set_mclk(state->internal, 135000000); - msleep(3); - - switch (state->internal->clkmode) { - case 0: - case 2: - stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | state->internal->clkmode); - break; - default: - selosci = 0x02 & stv0900_read_reg(state->internal, R0900_SYNTCTRL); - stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | selosci); - break; - } - msleep(3); + intp = state->internal; - state->internal->mclk = stv0900_get_mclk_freq(state->internal, state->internal->quartz); - if (state->internal->errs) - error = STV0900_I2C_ERROR; - } - } else { - error = STV0900_INVALID_HANDLE; - } + intp->demod_mode = p_init->demod_mode; + stv0900_st_dvbs2_single(intp, intp->demod_mode, STV0900_DEMOD_1); + intp->chip_id = stv0900_read_reg(intp, R0900_MID); + intp->rolloff = p_init->rolloff; + intp->quartz = p_init->dmd_ref_clk; + + stv0900_write_bits(intp, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff); + stv0900_write_bits(intp, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff); + + intp->ts_config = p_init->ts_config; + if (intp->ts_config == NULL) + stv0900_set_ts_parallel_serial(intp, + p_init->path1_ts_clock, + p_init->path2_ts_clock); + else { + for (i = 0; intp->ts_config[i].addr != 0xffff; i++) + stv0900_write_reg(intp, + intp->ts_config[i].addr, + intp->ts_config[i].val); + + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P2_RST_HWARE, 0); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 1); + stv0900_write_bits(intp, F0900_P1_RST_HWARE, 0); + } + + stv0900_write_bits(intp, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress); + switch (p_init->tuner1_adc) { + case 1: + stv0900_write_reg(intp, R0900_TSTTNR1, 0x26); + break; + default: + break; + } + + stv0900_write_bits(intp, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress); + switch (p_init->tuner2_adc) { + case 1: + stv0900_write_reg(intp, R0900_TSTTNR3, 0x26); + break; + default: + break; + } + + stv0900_write_bits(intp, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inv); + stv0900_write_bits(intp, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inv); + stv0900_set_mclk(intp, 135000000); + msleep(3); + + switch (intp->clkmode) { + case 0: + case 2: + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | intp->clkmode); + break; + default: + selosci = 0x02 & stv0900_read_reg(intp, R0900_SYNTCTRL); + stv0900_write_reg(intp, R0900_SYNTCTRL, 0x20 | selosci); + break; } + msleep(3); + + intp->mclk = stv0900_get_mclk_freq(intp, intp->quartz); + if (intp->errs) + error = STV0900_I2C_ERROR; return error; } -static int stv0900_status(struct stv0900_internal *i_params, +static int stv0900_status(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { enum fe_stv0900_search_state demod_state; - s32 mode_field, delin_field, lock_field, fifo_field, lockedvit_field; int locked = FALSE; - dmd_reg(mode_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF); - dmd_reg(delin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK); - dmd_reg(fifo_field, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK); - dmd_reg(lockedvit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT); - - demod_state = stv0900_get_bits(i_params, mode_field); + demod_state = stv0900_get_bits(intp, HEADER_MODE); switch (demod_state) { case STV0900_SEARCH: case STV0900_PLH_DETECTED: @@ -1525,17 +1460,19 @@ static int stv0900_status(struct stv0900_internal *i_params, locked = FALSE; break; case STV0900_DVBS2_FOUND: - locked = stv0900_get_bits(i_params, lock_field) && - stv0900_get_bits(i_params, delin_field) && - stv0900_get_bits(i_params, fifo_field); + locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && + stv0900_get_bits(intp, PKTDELIN_LOCK) && + stv0900_get_bits(intp, TSFIFO_LINEOK); break; case STV0900_DVBS_FOUND: - locked = stv0900_get_bits(i_params, lock_field) && - stv0900_get_bits(i_params, lockedvit_field) && - stv0900_get_bits(i_params, fifo_field); + locked = stv0900_get_bits(intp, LOCK_DEFINITIF) && + stv0900_get_bits(intp, LOCKEDVIT) && + stv0900_get_bits(intp, TSFIFO_LINEOK); break; } + dprintk("%s: locked = %d\n", __func__, locked); + return locked; } @@ -1543,7 +1480,8 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe, struct dvb_frontend_parameters *params) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; struct dtv_frontend_properties *c = &fe->dtv_property_cache; struct stv0900_search_params p_search; @@ -1551,10 +1489,16 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe, enum fe_stv0900_error error = STV0900_NO_ERROR; - dprintk(KERN_INFO "%s: ", __func__); + dprintk("%s: ", __func__); + + if (!(INRANGE(100000, c->symbol_rate, 70000000))) + return DVBFE_ALGO_SEARCH_FAILED; + + if (state->config->set_ts_params) + state->config->set_ts_params(fe, 0); p_result.locked = FALSE; - p_search.path = state->demod; + p_search.path = demod; p_search.frequency = c->frequency; p_search.symbol_rate = c->symbol_rate; p_search.search_range = 10000000; @@ -1563,103 +1507,47 @@ static enum dvbfe_search stv0900_search(struct dvb_frontend *fe, p_search.iq_inversion = STV0900_IQ_AUTO; p_search.search_algo = STV0900_BLIND_SEARCH; - if ((INRANGE(100000, p_search.symbol_rate, 70000000)) && - (INRANGE(100000, p_search.search_range, 50000000))) { - switch (p_search.path) { - case STV0900_DEMOD_1: - default: - i_params->dmd1_srch_standard = p_search.standard; - i_params->dmd1_symbol_rate = p_search.symbol_rate; - i_params->dmd1_srch_range = p_search.search_range; - i_params->tuner1_freq = p_search.frequency; - i_params->dmd1_srch_algo = p_search.search_algo; - i_params->dmd1_srch_iq_inv = p_search.iq_inversion; - i_params->dmd1_fec = p_search.fec; + intp->srch_standard[demod] = p_search.standard; + intp->symbol_rate[demod] = p_search.symbol_rate; + intp->srch_range[demod] = p_search.search_range; + intp->freq[demod] = p_search.frequency; + intp->srch_algo[demod] = p_search.search_algo; + intp->srch_iq_inv[demod] = p_search.iq_inversion; + intp->fec[demod] = p_search.fec; + if ((stv0900_algo(fe) == STV0900_RANGEOK) && + (intp->errs == STV0900_NO_ERROR)) { + p_result.locked = intp->result[demod].locked; + p_result.standard = intp->result[demod].standard; + p_result.frequency = intp->result[demod].frequency; + p_result.symbol_rate = intp->result[demod].symbol_rate; + p_result.fec = intp->result[demod].fec; + p_result.modcode = intp->result[demod].modcode; + p_result.pilot = intp->result[demod].pilot; + p_result.frame_len = intp->result[demod].frame_len; + p_result.spectrum = intp->result[demod].spectrum; + p_result.rolloff = intp->result[demod].rolloff; + p_result.modulation = intp->result[demod].modulation; + } else { + p_result.locked = FALSE; + switch (intp->err[demod]) { + case STV0900_I2C_ERROR: + error = STV0900_I2C_ERROR; break; - - case STV0900_DEMOD_2: - i_params->dmd2_srch_stndrd = p_search.standard; - i_params->dmd2_symbol_rate = p_search.symbol_rate; - i_params->dmd2_srch_range = p_search.search_range; - i_params->tuner2_freq = p_search.frequency; - i_params->dmd2_srch_algo = p_search.search_algo; - i_params->dmd2_srch_iq_inv = p_search.iq_inversion; - i_params->dmd2_fec = p_search.fec; + case STV0900_NO_ERROR: + default: + error = STV0900_SEARCH_FAILED; break; } - - if ((stv0900_algo(fe) == STV0900_RANGEOK) && - (i_params->errs == STV0900_NO_ERROR)) { - switch (p_search.path) { - case STV0900_DEMOD_1: - default: - p_result.locked = i_params->dmd1_rslts.locked; - p_result.standard = i_params->dmd1_rslts.standard; - p_result.frequency = i_params->dmd1_rslts.frequency; - p_result.symbol_rate = i_params->dmd1_rslts.symbol_rate; - p_result.fec = i_params->dmd1_rslts.fec; - p_result.modcode = i_params->dmd1_rslts.modcode; - p_result.pilot = i_params->dmd1_rslts.pilot; - p_result.frame_length = i_params->dmd1_rslts.frame_length; - p_result.spectrum = i_params->dmd1_rslts.spectrum; - p_result.rolloff = i_params->dmd1_rslts.rolloff; - p_result.modulation = i_params->dmd1_rslts.modulation; - break; - case STV0900_DEMOD_2: - p_result.locked = i_params->dmd2_rslts.locked; - p_result.standard = i_params->dmd2_rslts.standard; - p_result.frequency = i_params->dmd2_rslts.frequency; - p_result.symbol_rate = i_params->dmd2_rslts.symbol_rate; - p_result.fec = i_params->dmd2_rslts.fec; - p_result.modcode = i_params->dmd2_rslts.modcode; - p_result.pilot = i_params->dmd2_rslts.pilot; - p_result.frame_length = i_params->dmd2_rslts.frame_length; - p_result.spectrum = i_params->dmd2_rslts.spectrum; - p_result.rolloff = i_params->dmd2_rslts.rolloff; - p_result.modulation = i_params->dmd2_rslts.modulation; - break; - } - - } else { - p_result.locked = FALSE; - switch (p_search.path) { - case STV0900_DEMOD_1: - switch (i_params->dmd1_err) { - case STV0900_I2C_ERROR: - error = STV0900_I2C_ERROR; - break; - case STV0900_NO_ERROR: - default: - error = STV0900_SEARCH_FAILED; - break; - } - break; - case STV0900_DEMOD_2: - switch (i_params->dmd2_err) { - case STV0900_I2C_ERROR: - error = STV0900_I2C_ERROR; - break; - case STV0900_NO_ERROR: - default: - error = STV0900_SEARCH_FAILED; - break; - } - break; - } - } - - } else - error = STV0900_BAD_PARAMETER; + } if ((p_result.locked == TRUE) && (error == STV0900_NO_ERROR)) { - dprintk(KERN_INFO "Search Success\n"); + dprintk("Search Success\n"); return DVBFE_ALGO_SEARCH_SUCCESS; } else { - dprintk(KERN_INFO "Search Fail\n"); + dprintk("Search Fail\n"); return DVBFE_ALGO_SEARCH_FAILED; } - return DVBFE_ALGO_SEARCH_ERROR; } static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status) @@ -1690,16 +1578,13 @@ static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 rst_field; - - dmd_reg(rst_field, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE); if (stop_ts == TRUE) - stv0900_write_bits(i_params, rst_field, 1); + stv0900_write_bits(intp, RST_HWARE, 1); else - stv0900_write_bits(i_params, rst_field, 0); + stv0900_write_bits(intp, RST_HWARE, 0); return 0; } @@ -1707,23 +1592,19 @@ static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts) static int stv0900_diseqc_init(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 mode_field, reset_field; - dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE); - dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET); - - stv0900_write_bits(i_params, mode_field, state->config->diseqc_mode); - stv0900_write_bits(i_params, reset_field, 1); - stv0900_write_bits(i_params, reset_field, 0); + stv0900_write_bits(intp, DISTX_MODE, state->config->diseqc_mode); + stv0900_write_bits(intp, DISEQC_RESET, 1); + stv0900_write_bits(intp, DISEQC_RESET, 0); return 0; } static int stv0900_init(struct dvb_frontend *fe) { - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); stv0900_stop_ts(fe, 1); stv0900_diseqc_init(fe); @@ -1731,48 +1612,24 @@ static int stv0900_init(struct dvb_frontend *fe) return 0; } -static int stv0900_diseqc_send(struct stv0900_internal *i_params , u8 *Data, +static int stv0900_diseqc_send(struct stv0900_internal *intp , u8 *data, u32 NbData, enum fe_stv0900_demod_num demod) { s32 i = 0; - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 1); - while (i < NbData) { - while (stv0900_get_bits(i_params, F0900_P1_FIFO_FULL)) - ;/* checkpatch complains */ - stv0900_write_reg(i_params, R0900_P1_DISTXDATA, Data[i]); - i++; - } - - stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 0); - i = 0; - while ((stv0900_get_bits(i_params, F0900_P1_TX_IDLE) != 1) && (i < 10)) { - msleep(10); - i++; - } - - break; - case STV0900_DEMOD_2: - stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 1); - - while (i < NbData) { - while (stv0900_get_bits(i_params, F0900_P2_FIFO_FULL)) - ;/* checkpatch complains */ - stv0900_write_reg(i_params, R0900_P2_DISTXDATA, Data[i]); - i++; - } - - stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 0); - i = 0; - while ((stv0900_get_bits(i_params, F0900_P2_TX_IDLE) != 1) && (i < 10)) { - msleep(10); - i++; - } + stv0900_write_bits(intp, DIS_PRECHARGE, 1); + while (i < NbData) { + while (stv0900_get_bits(intp, FIFO_FULL)) + ;/* checkpatch complains */ + stv0900_write_reg(intp, DISTXDATA, data[i]); + i++; + } - break; + stv0900_write_bits(intp, DIS_PRECHARGE, 0); + i = 0; + while ((stv0900_get_bits(intp, TX_IDLE) != 1) && (i < 10)) { + msleep(10); + i++; } return 0; @@ -1792,22 +1649,21 @@ static int stv0900_send_master_cmd(struct dvb_frontend *fe, static int stv0900_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 mode_field; - u32 diseqc_fifo; + u8 data; - dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE); - dmd_reg(diseqc_fifo, R0900_P1_DISTXDATA, R0900_P2_DISTXDATA); switch (burst) { case SEC_MINI_A: - stv0900_write_bits(i_params, mode_field, 3);/* Unmodulated */ - stv0900_write_reg(i_params, diseqc_fifo, 0x00); + stv0900_write_bits(intp, DISTX_MODE, 3);/* Unmodulated */ + data = 0x00; + stv0900_diseqc_send(intp, &data, 1, state->demod); break; case SEC_MINI_B: - stv0900_write_bits(i_params, mode_field, 2);/* Modulated */ - stv0900_write_reg(i_params, diseqc_fifo, 0xff); + stv0900_write_bits(intp, DISTX_MODE, 2);/* Modulated */ + data = 0xff; + stv0900_diseqc_send(intp, &data, 1, state->demod); break; } @@ -1818,68 +1674,54 @@ static int stv0900_recv_slave_reply(struct dvb_frontend *fe, struct dvb_diseqc_slave_reply *reply) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; s32 i = 0; - switch (state->demod) { - case STV0900_DEMOD_1: - default: - reply->msg_len = 0; - - while ((stv0900_get_bits(i_params, F0900_P1_RX_END) != 1) && (i < 10)) { - msleep(10); - i++; - } - - if (stv0900_get_bits(i_params, F0900_P1_RX_END)) { - reply->msg_len = stv0900_get_bits(i_params, F0900_P1_FIFO_BYTENBR); - - for (i = 0; i < reply->msg_len; i++) - reply->msg[i] = stv0900_read_reg(i_params, R0900_P1_DISRXDATA); - } - break; - case STV0900_DEMOD_2: - reply->msg_len = 0; + reply->msg_len = 0; - while ((stv0900_get_bits(i_params, F0900_P2_RX_END) != 1) && (i < 10)) { - msleep(10); - i++; - } + while ((stv0900_get_bits(intp, RX_END) != 1) && (i < 10)) { + msleep(10); + i++; + } - if (stv0900_get_bits(i_params, F0900_P2_RX_END)) { - reply->msg_len = stv0900_get_bits(i_params, F0900_P2_FIFO_BYTENBR); + if (stv0900_get_bits(intp, RX_END)) { + reply->msg_len = stv0900_get_bits(intp, FIFO_BYTENBR); - for (i = 0; i < reply->msg_len; i++) - reply->msg[i] = stv0900_read_reg(i_params, R0900_P2_DISRXDATA); - } - break; + for (i = 0; i < reply->msg_len; i++) + reply->msg[i] = stv0900_read_reg(intp, DISRXDATA); } return 0; } -static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone) +static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t toneoff) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 mode_field, reset_field; - - dprintk(KERN_INFO "%s: %s\n", __func__, ((tone == 0) ? "Off" : "On")); - dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE); - dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET); + dprintk("%s: %s\n", __func__, ((toneoff == 0) ? "On" : "Off")); - if (tone) { - /*Set the DiseqC mode to 22Khz continues tone*/ - stv0900_write_bits(i_params, mode_field, 0); - stv0900_write_bits(i_params, reset_field, 1); + switch (toneoff) { + case SEC_TONE_ON: + /*Set the DiseqC mode to 22Khz _continues_ tone*/ + stv0900_write_bits(intp, DISTX_MODE, 0); + stv0900_write_bits(intp, DISEQC_RESET, 1); /*release DiseqC reset to enable the 22KHz tone*/ - stv0900_write_bits(i_params, reset_field, 0); - } else { - stv0900_write_bits(i_params, mode_field, 0); + stv0900_write_bits(intp, DISEQC_RESET, 0); + break; + case SEC_TONE_OFF: + /*return diseqc mode to config->diseqc_mode. + Usually it's without _continues_ tone */ + stv0900_write_bits(intp, DISTX_MODE, + state->config->diseqc_mode); /*maintain the DiseqC reset to disable the 22KHz tone*/ - stv0900_write_bits(i_params, reset_field, 1); + stv0900_write_bits(intp, DISEQC_RESET, 1); + stv0900_write_bits(intp, DISEQC_RESET, 0); + break; + default: + return -EINVAL; } return 0; @@ -1889,11 +1731,11 @@ static void stv0900_release(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); if ((--(state->internal->dmds_used)) <= 0) { - dprintk(KERN_INFO "%s: Actually removing\n", __func__); + dprintk("%s: Actually removing\n", __func__); remove_inode(state->internal); kfree(state->internal); @@ -1963,17 +1805,17 @@ struct dvb_frontend *stv0900_attach(const struct stv0900_config *config, case 0: case 1: init_params.dmd_ref_clk = config->xtal; - init_params.demod_mode = STV0900_DUAL; + init_params.demod_mode = config->demod_mode; init_params.rolloff = STV0900_35; init_params.path1_ts_clock = config->path1_mode; init_params.tun1_maddress = config->tun1_maddress; - init_params.tun1_iq_inversion = STV0900_IQ_NORMAL; + init_params.tun1_iq_inv = STV0900_IQ_NORMAL; init_params.tuner1_adc = config->tun1_adc; init_params.path2_ts_clock = config->path2_mode; init_params.ts_config = config->ts_config_regs; init_params.tun2_maddress = config->tun2_maddress; init_params.tuner2_adc = config->tun2_adc; - init_params.tun2_iq_inversion = STV0900_IQ_SWAPPED; + init_params.tun2_iq_inv = STV0900_IQ_SWAPPED; err_stv0900 = stv0900_init_internal(&state->frontend, &init_params); diff --git a/drivers/media/dvb/frontends/stv0900_init.h b/drivers/media/dvb/frontends/stv0900_init.h index ff388b47a4e39..b684df9995d82 100644 --- a/drivers/media/dvb/frontends/stv0900_init.h +++ b/drivers/media/dvb/frontends/stv0900_init.h @@ -141,85 +141,228 @@ struct stv0900_short_frames_car_loop_optim { }; +struct stv0900_short_frames_car_loop_optim_vs_mod { + enum fe_stv0900_modulation modulation; + u8 car_loop_2; /* SR<3msps */ + u8 car_loop_5; /* 3dmd1_srch_range / 1000; - break; - case STV0900_DEMOD_2: - carr_offset = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8) - | stv0900_read_reg(i_params, - R0900_P2_CFR1); - carr_offset = ge2comp(carr_offset, 16); - agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8) - | stv0900_read_reg(i_params, - R0900_P2_AGC2I0); - max_carrier = i_params->dmd2_srch_range / 1000; - break; - } + carr_offset = (stv0900_read_reg(intp, CFR2) << 8) + | stv0900_read_reg(intp, CFR1); + carr_offset = ge2comp(carr_offset, 16); + agc2_integr = (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); + max_carrier = intp->srch_range[demod] / 1000; max_carrier += (max_carrier / 10); max_carrier = 65536 * (max_carrier / 2); - max_carrier /= i_params->mclk / 1000; + max_carrier /= intp->mclk / 1000; if (max_carrier > 0x4000) max_carrier = 0x4000; if ((agc2_integr > 0x2000) - || (carr_offset > + 2*max_carrier) - || (carr_offset < -2*max_carrier)) + || (carr_offset > (2 * max_carrier)) + || (carr_offset < (-2 * max_carrier))) no_signal = TRUE; - else - no_signal = FALSE; return no_signal; } -static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params, +static void stv0900_get_sw_loop_params(struct stv0900_internal *intp, s32 *frequency_inc, s32 *sw_timeout, s32 *steps, enum fe_stv0900_demod_num demod) @@ -85,30 +74,19 @@ static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params, enum fe_stv0900_search_standard standard; - switch (demod) { - case STV0900_DEMOD_1: - default: - srate = i_params->dmd1_symbol_rate; - max_carrier = i_params->dmd1_srch_range / 1000; - max_carrier += max_carrier / 10; - standard = i_params->dmd1_srch_standard; - break; - case STV0900_DEMOD_2: - srate = i_params->dmd2_symbol_rate; - max_carrier = i_params->dmd2_srch_range / 1000; - max_carrier += max_carrier / 10; - standard = i_params->dmd2_srch_stndrd; - break; - } + srate = intp->symbol_rate[demod]; + max_carrier = intp->srch_range[demod] / 1000; + max_carrier += max_carrier / 10; + standard = intp->srch_standard[demod]; max_carrier = 65536 * (max_carrier / 2); - max_carrier /= i_params->mclk / 1000; + max_carrier /= intp->mclk / 1000; if (max_carrier > 0x4000) max_carrier = 0x4000; freq_inc = srate; - freq_inc /= i_params->mclk >> 10; + freq_inc /= intp->mclk >> 10; freq_inc = freq_inc << 6; switch (standard) { @@ -154,7 +132,7 @@ static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params, } -static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, +static int stv0900_search_carr_sw_loop(struct stv0900_internal *intp, s32 FreqIncr, s32 Timeout, int zigzag, s32 MaxStep, enum fe_stv0900_demod_num demod) { @@ -164,20 +142,11 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, freqOffset, max_carrier; - switch (demod) { - case STV0900_DEMOD_1: - default: - max_carrier = i_params->dmd1_srch_range / 1000; - max_carrier += (max_carrier / 10); - break; - case STV0900_DEMOD_2: - max_carrier = i_params->dmd2_srch_range / 1000; - max_carrier += (max_carrier / 10); - break; - } + max_carrier = intp->srch_range[demod] / 1000; + max_carrier += (max_carrier / 10); max_carrier = 65536 * (max_carrier / 2); - max_carrier /= i_params->mclk / 1000; + max_carrier /= intp->mclk / 1000; if (max_carrier > 0x4000) max_carrier = 0x4000; @@ -190,40 +159,15 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, stepCpt = 0; do { - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, - (freqOffset / 256) & 0xFF); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, - freqOffset & 0xFF); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1); - - if (i_params->chip_id == 0x12) { - stv0900_write_bits(i_params, - F0900_P1_RST_HWARE, 1); - stv0900_write_bits(i_params, - F0900_P1_RST_HWARE, 0); - } - break; - case STV0900_DEMOD_2: - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, - (freqOffset / 256) & 0xFF); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, - freqOffset & 0xFF); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1); - - if (i_params->chip_id == 0x12) { - stv0900_write_bits(i_params, - F0900_P2_RST_HWARE, 1); - stv0900_write_bits(i_params, - F0900_P2_RST_HWARE, 0); - } - break; + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, (freqOffset / 256) & 0xff); + stv0900_write_reg(intp, CFRINIT0, freqOffset & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x18); + stv0900_write_bits(intp, ALGOSWRST, 1); + + if (intp->chip_id == 0x12) { + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); } if (zigzag == TRUE) { @@ -235,8 +179,8 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, freqOffset += + 2 * FreqIncr; stepCpt++; - lock = stv0900_get_demod_lock(i_params, demod, Timeout); - no_signal = stv0900_check_signal_presence(i_params, demod); + lock = stv0900_get_demod_lock(intp, demod, Timeout); + no_signal = stv0900_check_signal_presence(intp, demod); } while ((lock == FALSE) && (no_signal == FALSE) @@ -244,269 +188,138 @@ static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params, && ((freqOffset + FreqIncr) > -max_carrier) && (stepCpt < MaxStep)); - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0); - break; - case STV0900_DEMOD_2: - stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0); - break; - } + stv0900_write_bits(intp, ALGOSWRST, 0); return lock; } -int stv0900_sw_algo(struct stv0900_internal *i_params, +int stv0900_sw_algo(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - int lock = FALSE; - - int no_signal, - zigzag; - s32 dvbs2_fly_wheel; - - s32 freqIncrement, softStepTimeout, trialCounter, max_steps; - - stv0900_get_sw_loop_params(i_params, &freqIncrement, &softStepTimeout, + int lock = FALSE, + no_signal, + zigzag; + s32 s2fw, + fqc_inc, + sft_stp_tout, + trial_cntr, + max_steps; + + stv0900_get_sw_loop_params(intp, &fqc_inc, &sft_stp_tout, &max_steps, demod); - switch (demod) { - case STV0900_DEMOD_1: - default: - switch (i_params->dmd1_srch_standard) { - case STV0900_SEARCH_DVBS1: - case STV0900_SEARCH_DSS: - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CARFREQ, - 0x3B); - else - stv0900_write_reg(i_params, R0900_P1_CARFREQ, - 0xef); - - stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x49); - zigzag = FALSE; - break; - case STV0900_SEARCH_DVBS2: - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CORRELABS, - 0x79); - else - stv0900_write_reg(i_params, R0900_P1_CORRELABS, - 0x68); + switch (intp->srch_standard[demod]) { + case STV0900_SEARCH_DVBS1: + case STV0900_SEARCH_DSS: + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x3b); + else + stv0900_write_reg(intp, CARFREQ, 0xef); - stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, - 0x89); + stv0900_write_reg(intp, DMDCFGMD, 0x49); + zigzag = FALSE; + break; + case STV0900_SEARCH_DVBS2: + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CORRELABS, 0x79); + else + stv0900_write_reg(intp, CORRELABS, 0x68); - zigzag = TRUE; - break; - case STV0900_AUTO_SEARCH: - default: - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, - 0x3B); - stv0900_write_reg(i_params, R0900_P1_CORRELABS, - 0x79); - } else { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, - 0xef); - stv0900_write_reg(i_params, R0900_P1_CORRELABS, - 0x68); - } + stv0900_write_reg(intp, DMDCFGMD, 0x89); - stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, - 0xc9); - zigzag = FALSE; - break; + zigzag = TRUE; + break; + case STV0900_AUTO_SEARCH: + default: + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x3b); + stv0900_write_reg(intp, CORRELABS, 0x79); + } else { + stv0900_write_reg(intp, CARFREQ, 0xef); + stv0900_write_reg(intp, CORRELABS, 0x68); } - trialCounter = 0; - do { - lock = stv0900_search_carr_sw_loop(i_params, - freqIncrement, - softStepTimeout, - zigzag, - max_steps, - demod); - no_signal = stv0900_check_signal_presence(i_params, - demod); - trialCounter++; - if ((lock == TRUE) - || (no_signal == TRUE) - || (trialCounter == 2)) { - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, - R0900_P1_CARFREQ, - 0x49); - stv0900_write_reg(i_params, - R0900_P1_CORRELABS, - 0x9e); - } else { - stv0900_write_reg(i_params, - R0900_P1_CARFREQ, - 0xed); - stv0900_write_reg(i_params, - R0900_P1_CORRELABS, - 0x88); - } - - if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS2_FOUND)) { - msleep(softStepTimeout); - dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT); - - if (dvbs2_fly_wheel < 0xd) { - msleep(softStepTimeout); - dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT); - } - - if (dvbs2_fly_wheel < 0xd) { - lock = FALSE; - - if (trialCounter < 2) { - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x79); - else - stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x68); - - stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x89); - } - } - } - } - - } while ((lock == FALSE) - && (trialCounter < 2) - && (no_signal == FALSE)); - + stv0900_write_reg(intp, DMDCFGMD, 0xc9); + zigzag = FALSE; break; - case STV0900_DEMOD_2: - switch (i_params->dmd2_srch_stndrd) { - case STV0900_SEARCH_DVBS1: - case STV0900_SEARCH_DSS: - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CARFREQ, - 0x3b); - else - stv0900_write_reg(i_params, R0900_P2_CARFREQ, - 0xef); - - stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, - 0x49); - zigzag = FALSE; - break; - case STV0900_SEARCH_DVBS2: - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CORRELABS, - 0x79); - else - stv0900_write_reg(i_params, R0900_P2_CORRELABS, - 0x68); + } - stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89); - zigzag = TRUE; - break; - case STV0900_AUTO_SEARCH: - default: - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, - 0x3b); - stv0900_write_reg(i_params, R0900_P2_CORRELABS, - 0x79); + trial_cntr = 0; + do { + lock = stv0900_search_carr_sw_loop(intp, + fqc_inc, + sft_stp_tout, + zigzag, + max_steps, + demod); + no_signal = stv0900_check_signal_presence(intp, demod); + trial_cntr++; + if ((lock == TRUE) + || (no_signal == TRUE) + || (trial_cntr == 2)) { + + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x49); + stv0900_write_reg(intp, CORRELABS, 0x9e); } else { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, - 0xef); - stv0900_write_reg(i_params, R0900_P2_CORRELABS, - 0x68); + stv0900_write_reg(intp, CARFREQ, 0xed); + stv0900_write_reg(intp, CORRELABS, 0x88); } - stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0xc9); - - zigzag = FALSE; - break; - } + if ((stv0900_get_bits(intp, HEADER_MODE) == + STV0900_DVBS2_FOUND) && + (lock == TRUE)) { + msleep(sft_stp_tout); + s2fw = stv0900_get_bits(intp, FLYWHEEL_CPT); - trialCounter = 0; - - do { - lock = stv0900_search_carr_sw_loop(i_params, - freqIncrement, - softStepTimeout, - zigzag, - max_steps, - demod); - no_signal = stv0900_check_signal_presence(i_params, - demod); - trialCounter++; - if ((lock == TRUE) - || (no_signal == TRUE) - || (trialCounter == 2)) { - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, - R0900_P2_CARFREQ, - 0x49); - stv0900_write_reg(i_params, - R0900_P2_CORRELABS, - 0x9e); - } else { - stv0900_write_reg(i_params, - R0900_P2_CARFREQ, - 0xed); - stv0900_write_reg(i_params, - R0900_P2_CORRELABS, - 0x88); + if (s2fw < 0xd) { + msleep(sft_stp_tout); + s2fw = stv0900_get_bits(intp, + FLYWHEEL_CPT); } - if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS2_FOUND)) { - msleep(softStepTimeout); - dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT); - if (dvbs2_fly_wheel < 0xd) { - msleep(softStepTimeout); - dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT); - } + if (s2fw < 0xd) { + lock = FALSE; - if (dvbs2_fly_wheel < 0xd) { - lock = FALSE; - if (trialCounter < 2) { - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x79); - else - stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x68); + if (trial_cntr < 2) { + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, + CORRELABS, + 0x79); + else + stv0900_write_reg(intp, + CORRELABS, + 0x68); - stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89); - } + stv0900_write_reg(intp, + DMDCFGMD, + 0x89); } } } + } - } while ((lock == FALSE) && (trialCounter < 2) && (no_signal == FALSE)); - - break; - } + } while ((lock == FALSE) + && (trial_cntr < 2) + && (no_signal == FALSE)); return lock; } -static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params, +static u32 stv0900_get_symbol_rate(struct stv0900_internal *intp, u32 mclk, enum fe_stv0900_demod_num demod) { - s32 sfr_field3, sfr_field2, sfr_field1, sfr_field0, - rem1, rem2, intval1, intval2, srate; - - dmd_reg(sfr_field3, F0900_P1_SYMB_FREQ3, F0900_P2_SYMB_FREQ3); - dmd_reg(sfr_field2, F0900_P1_SYMB_FREQ2, F0900_P2_SYMB_FREQ2); - dmd_reg(sfr_field1, F0900_P1_SYMB_FREQ1, F0900_P2_SYMB_FREQ1); - dmd_reg(sfr_field0, F0900_P1_SYMB_FREQ0, F0900_P2_SYMB_FREQ0); - - srate = (stv0900_get_bits(i_params, sfr_field3) << 24) + - (stv0900_get_bits(i_params, sfr_field2) << 16) + - (stv0900_get_bits(i_params, sfr_field1) << 8) + - (stv0900_get_bits(i_params, sfr_field0)); + s32 rem1, rem2, intval1, intval2, srate; + + srate = (stv0900_get_bits(intp, SYMB_FREQ3) << 24) + + (stv0900_get_bits(intp, SYMB_FREQ2) << 16) + + (stv0900_get_bits(intp, SYMB_FREQ1) << 8) + + (stv0900_get_bits(intp, SYMB_FREQ0)); dprintk("lock: srate=%d r0=0x%x r1=0x%x r2=0x%x r3=0x%x \n", - srate, stv0900_get_bits(i_params, sfr_field0), - stv0900_get_bits(i_params, sfr_field1), - stv0900_get_bits(i_params, sfr_field2), - stv0900_get_bits(i_params, sfr_field3)); + srate, stv0900_get_bits(intp, SYMB_FREQ0), + stv0900_get_bits(intp, SYMB_FREQ1), + stv0900_get_bits(intp, SYMB_FREQ2), + stv0900_get_bits(intp, SYMB_FREQ3)); intval1 = (mclk) >> 16; intval2 = (srate) >> 16; @@ -520,18 +333,15 @@ static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params, return srate; } -static void stv0900_set_symbol_rate(struct stv0900_internal *i_params, +static void stv0900_set_symbol_rate(struct stv0900_internal *intp, u32 mclk, u32 srate, enum fe_stv0900_demod_num demod) { - s32 sfr_init_reg; u32 symb; - dprintk(KERN_INFO "%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk, + dprintk("%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk, srate, demod); - dmd_reg(sfr_init_reg, R0900_P1_SFRINIT1, R0900_P2_SFRINIT1); - if (srate > 60000000) { symb = srate << 4; symb /= (mclk >> 12); @@ -543,19 +353,16 @@ static void stv0900_set_symbol_rate(struct stv0900_internal *i_params, symb /= (mclk >> 7); } - stv0900_write_reg(i_params, sfr_init_reg, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, sfr_init_reg + 1, (symb & 0xFF)); + stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0x7f); + stv0900_write_reg(intp, SFRINIT1 + 1, (symb & 0xff)); } -static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params, +static void stv0900_set_max_symbol_rate(struct stv0900_internal *intp, u32 mclk, u32 srate, enum fe_stv0900_demod_num demod) { - s32 sfr_max_reg; u32 symb; - dmd_reg(sfr_max_reg, R0900_P1_SFRUP1, R0900_P2_SFRUP1); - srate = 105 * (srate / 100); if (srate > 60000000) { @@ -570,23 +377,20 @@ static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params, } if (symb < 0x7fff) { - stv0900_write_reg(i_params, sfr_max_reg, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, sfr_max_reg + 1, (symb & 0xFF)); + stv0900_write_reg(intp, SFRUP1, (symb >> 8) & 0x7f); + stv0900_write_reg(intp, SFRUP1 + 1, (symb & 0xff)); } else { - stv0900_write_reg(i_params, sfr_max_reg, 0x7F); - stv0900_write_reg(i_params, sfr_max_reg + 1, 0xFF); + stv0900_write_reg(intp, SFRUP1, 0x7f); + stv0900_write_reg(intp, SFRUP1 + 1, 0xff); } } -static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params, +static void stv0900_set_min_symbol_rate(struct stv0900_internal *intp, u32 mclk, u32 srate, enum fe_stv0900_demod_num demod) { - s32 sfr_min_reg; u32 symb; - dmd_reg(sfr_min_reg, R0900_P1_SFRLOW1, R0900_P2_SFRLOW1); - srate = 95 * (srate / 100); if (srate > 60000000) { symb = srate << 4; @@ -601,22 +405,20 @@ static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params, symb /= (mclk >> 7); } - stv0900_write_reg(i_params, sfr_min_reg, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, sfr_min_reg + 1, (symb & 0xFF)); + stv0900_write_reg(intp, SFRLOW1, (symb >> 8) & 0xff); + stv0900_write_reg(intp, SFRLOW1 + 1, (symb & 0xff)); } -static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params, +static s32 stv0900_get_timing_offst(struct stv0900_internal *intp, u32 srate, enum fe_stv0900_demod_num demod) { - s32 tmgreg, - timingoffset; + s32 timingoffset; - dmd_reg(tmgreg, R0900_P1_TMGREG2, R0900_P2_TMGREG2); - timingoffset = (stv0900_read_reg(i_params, tmgreg) << 16) + - (stv0900_read_reg(i_params, tmgreg + 1) << 8) + - (stv0900_read_reg(i_params, tmgreg + 2)); + timingoffset = (stv0900_read_reg(intp, TMGREG2) << 16) + + (stv0900_read_reg(intp, TMGREG2 + 1) << 8) + + (stv0900_read_reg(intp, TMGREG2 + 2)); timingoffset = ge2comp(timingoffset, 24); @@ -630,22 +432,19 @@ static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params, return timingoffset; } -static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *i_params, +static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - s32 rolloff, man_fld, matstr_reg, rolloff_ctl_fld; - - dmd_reg(man_fld, F0900_P1_MANUAL_ROLLOFF, F0900_P2_MANUAL_ROLLOFF); - dmd_reg(matstr_reg, R0900_P1_MATSTR1, R0900_P2_MATSTR1); - dmd_reg(rolloff_ctl_fld, F0900_P1_ROLLOFF_CONTROL, - F0900_P2_ROLLOFF_CONTROL); - - if (i_params->chip_id == 0x10) { - stv0900_write_bits(i_params, man_fld, 1); - rolloff = stv0900_read_reg(i_params, matstr_reg) & 0x03; - stv0900_write_bits(i_params, rolloff_ctl_fld, rolloff); - } else - stv0900_write_bits(i_params, man_fld, 0); + s32 rolloff; + + if (intp->chip_id == 0x10) { + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); + rolloff = stv0900_read_reg(intp, MATSTR1) & 0x03; + stv0900_write_bits(intp, ROLLOFF_CONTROL, rolloff); + } else if (intp->chip_id <= 0x20) + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 0); + else /* cut 3.0 */ + stv0900_write_bits(intp, MANUALS2_ROLLOFF, 0); } static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro) @@ -668,84 +467,47 @@ static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro) return srate + (srate * rolloff) / 100; } -static int stv0900_check_timing_lock(struct stv0900_internal *i_params, +static int stv0900_check_timing_lock(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { int timingLock = FALSE; - s32 i, - timingcpt = 0; - u8 carFreq, - tmgTHhigh, - tmgTHLow; - - switch (demod) { - case STV0900_DEMOD_1: - default: - carFreq = stv0900_read_reg(i_params, R0900_P1_CARFREQ); - tmgTHhigh = stv0900_read_reg(i_params, R0900_P1_TMGTHRISE); - tmgTHLow = stv0900_read_reg(i_params, R0900_P1_TMGTHFALL); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x0); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P1_RTC, 0x80); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x40); - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x0); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0x0); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0x0); - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x65); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - msleep(7); - - for (i = 0; i < 10; i++) { - if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2) - timingcpt++; - - msleep(1); - } - - if (timingcpt >= 3) - timingLock = TRUE; - - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38); - stv0900_write_reg(i_params, R0900_P1_RTC, 0x88); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68); - stv0900_write_reg(i_params, R0900_P1_CARFREQ, carFreq); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, tmgTHhigh); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, tmgTHLow); - break; - case STV0900_DEMOD_2: - carFreq = stv0900_read_reg(i_params, R0900_P2_CARFREQ); - tmgTHhigh = stv0900_read_reg(i_params, R0900_P2_TMGTHRISE); - tmgTHLow = stv0900_read_reg(i_params, R0900_P2_TMGTHFALL); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P2_RTC, 0x80); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x40); - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x0); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0x0); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0x0); - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x65); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - msleep(5); - for (i = 0; i < 10; i++) { - if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2) - timingcpt++; - - msleep(1); - } + s32 i, + timingcpt = 0; + u8 car_freq, + tmg_th_high, + tmg_th_low; + + car_freq = stv0900_read_reg(intp, CARFREQ); + tmg_th_high = stv0900_read_reg(intp, TMGTHRISE); + tmg_th_low = stv0900_read_reg(intp, TMGTHFALL); + stv0900_write_reg(intp, TMGTHRISE, 0x20); + stv0900_write_reg(intp, TMGTHFALL, 0x0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, RTC, 0x80); + stv0900_write_reg(intp, RTCS2, 0x40); + stv0900_write_reg(intp, CARFREQ, 0x0); + stv0900_write_reg(intp, CFRINIT1, 0x0); + stv0900_write_reg(intp, CFRINIT0, 0x0); + stv0900_write_reg(intp, AGC2REF, 0x65); + stv0900_write_reg(intp, DMDISTATE, 0x18); + msleep(7); + + for (i = 0; i < 10; i++) { + if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) + timingcpt++; + + msleep(1); + } - if (timingcpt >= 3) - timingLock = TRUE; + if (timingcpt >= 3) + timingLock = TRUE; - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38); - stv0900_write_reg(i_params, R0900_P2_RTC, 0x88); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68); - stv0900_write_reg(i_params, R0900_P2_CARFREQ, carFreq); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, tmgTHhigh); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, tmgTHLow); - break; - } + stv0900_write_reg(intp, AGC2REF, 0x38); + stv0900_write_reg(intp, RTC, 0x88); + stv0900_write_reg(intp, RTCS2, 0x68); + stv0900_write_reg(intp, CARFREQ, car_freq); + stv0900_write_reg(intp, TMGTHRISE, tmg_th_high); + stv0900_write_reg(intp, TMGTHFALL, tmg_th_low); return timingLock; } @@ -754,142 +516,114 @@ static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe, s32 demod_timeout) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; + int lock = FALSE, + d = demod; + s32 srate, + search_range, + locktimeout, + currier_step, + nb_steps, + current_step, + direction, + tuner_freq, + timeout, + freq; - int lock = FALSE; - s32 srate, search_range, locktimeout, - currier_step, nb_steps, current_step, - direction, tuner_freq, timeout; - - switch (demod) { - case STV0900_DEMOD_1: - default: - srate = i_params->dmd1_symbol_rate; - search_range = i_params->dmd1_srch_range; - break; - - case STV0900_DEMOD_2: - srate = i_params->dmd2_symbol_rate; - search_range = i_params->dmd2_srch_range; - break; - } + srate = intp->symbol_rate[d]; + search_range = intp->srch_range[d]; if (srate >= 10000000) locktimeout = demod_timeout / 3; else locktimeout = demod_timeout / 2; - lock = stv0900_get_demod_lock(i_params, demod, locktimeout); - - if (lock == FALSE) { - if (srate >= 10000000) { - if (stv0900_check_timing_lock(i_params, demod) == TRUE) { - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15); - break; - case STV0900_DEMOD_2: - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15); - break; - } + lock = stv0900_get_demod_lock(intp, d, locktimeout); - lock = stv0900_get_demod_lock(i_params, demod, demod_timeout); - } else - lock = FALSE; - } else { - if (srate <= 4000000) - currier_step = 1000; - else if (srate <= 7000000) - currier_step = 2000; - else if (srate <= 10000000) - currier_step = 3000; - else - currier_step = 5000; - - nb_steps = ((search_range / 1000) / currier_step); - nb_steps /= 2; - nb_steps = (2 * (nb_steps + 1)); - if (nb_steps < 0) - nb_steps = 2; - else if (nb_steps > 12) - nb_steps = 12; - - current_step = 1; - direction = 1; + if (lock != FALSE) + return lock; + + if (srate >= 10000000) { + if (stv0900_check_timing_lock(intp, d) == TRUE) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + lock = stv0900_get_demod_lock(intp, d, demod_timeout); + } else + lock = FALSE; + + return lock; + } + + if (intp->chip_id <= 0x20) { + if (srate <= 1000000) + currier_step = 500; + else if (srate <= 4000000) + currier_step = 1000; + else if (srate <= 7000000) + currier_step = 2000; + else if (srate <= 10000000) + currier_step = 3000; + else + currier_step = 5000; + + if (srate >= 2000000) { timeout = (demod_timeout / 3); if (timeout > 1000) timeout = 1000; + } else + timeout = (demod_timeout / 2); + } else { + /*cut 3.0 */ + currier_step = srate / 4000; + timeout = (demod_timeout * 3) / 4; + } - switch (demod) { - case STV0900_DEMOD_1: - default: - if (lock == FALSE) { - tuner_freq = i_params->tuner1_freq; - i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + i_params->dmd1_symbol_rate; + nb_steps = ((search_range / 1000) / currier_step); - while ((current_step <= nb_steps) && (lock == FALSE)) { + if ((nb_steps % 2) != 0) + nb_steps += 1; - if (direction > 0) - tuner_freq += (current_step * currier_step); - else - tuner_freq -= (current_step * currier_step); - - stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C); - if (i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS2) { - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); - } - - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15); - lock = stv0900_get_demod_lock(i_params, demod, timeout); - direction *= -1; - current_step++; - } - } - break; - case STV0900_DEMOD_2: - if (lock == FALSE) { - tuner_freq = i_params->tuner2_freq; - i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + srate; + if (nb_steps <= 0) + nb_steps = 2; + else if (nb_steps > 12) + nb_steps = 12; - while ((current_step <= nb_steps) && (lock == FALSE)) { + current_step = 1; + direction = 1; - if (direction > 0) - tuner_freq += (current_step * currier_step); - else - tuner_freq -= (current_step * currier_step); - - stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C); - if (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS2) { - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - } - - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15); - lock = stv0900_get_demod_lock(i_params, demod, timeout); - direction *= -1; - current_step++; - } - } - break; - } + if (intp->chip_id <= 0x20) { + tuner_freq = intp->freq[d]; + intp->bw[d] = stv0900_carrier_width(intp->symbol_rate[d], + intp->rolloff) + intp->symbol_rate[d]; + } else + tuner_freq = 0; + + while ((current_step <= nb_steps) && (lock == FALSE)) { + if (direction > 0) + tuner_freq += (current_step * currier_step); + else + tuner_freq -= (current_step * currier_step); + + if (intp->chip_id <= 0x20) { + stv0900_set_tuner(fe, tuner_freq, intp->bw[d]); + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, 0); + stv0900_write_reg(intp, CFRINIT0, 0); + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x15); + } else { + stv0900_write_reg(intp, DMDISTATE, 0x1c); + freq = (tuner_freq * 65536) / (intp->mclk / 1000); + stv0900_write_bits(intp, CFR_INIT1, MSB(freq)); + stv0900_write_bits(intp, CFR_INIT0, LSB(freq)); + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, DMDISTATE, 0x05); } + + lock = stv0900_get_demod_lock(intp, d, timeout); + direction *= -1; + current_step++; } return lock; @@ -931,9 +665,7 @@ static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout, } else if (srate <= 20000000) { (*demod_timeout) = 400; (*fec_timeout) = 130; - } - - else { + } else { (*demod_timeout) = 300; (*fec_timeout) = 100; } @@ -946,95 +678,77 @@ static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout, (*demod_timeout) /= 2; } -static void stv0900_set_viterbi_tracq(struct stv0900_internal *i_params, +static void stv0900_set_viterbi_tracq(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - s32 vth_reg; - - dprintk(KERN_INFO "%s\n", __func__); + s32 vth_reg = VTH12; - dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12); + dprintk("%s\n", __func__); - stv0900_write_reg(i_params, vth_reg++, 0xd0); - stv0900_write_reg(i_params, vth_reg++, 0x7d); - stv0900_write_reg(i_params, vth_reg++, 0x53); - stv0900_write_reg(i_params, vth_reg++, 0x2F); - stv0900_write_reg(i_params, vth_reg++, 0x24); - stv0900_write_reg(i_params, vth_reg++, 0x1F); + stv0900_write_reg(intp, vth_reg++, 0xd0); + stv0900_write_reg(intp, vth_reg++, 0x7d); + stv0900_write_reg(intp, vth_reg++, 0x53); + stv0900_write_reg(intp, vth_reg++, 0x2f); + stv0900_write_reg(intp, vth_reg++, 0x24); + stv0900_write_reg(intp, vth_reg++, 0x1f); } -static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params, - enum fe_stv0900_search_standard Standard, - enum fe_stv0900_fec PunctureRate, +static void stv0900_set_viterbi_standard(struct stv0900_internal *intp, + enum fe_stv0900_search_standard standard, + enum fe_stv0900_fec fec, enum fe_stv0900_demod_num demod) { + dprintk("%s: ViterbiStandard = ", __func__); - s32 fecmReg, - prvitReg; - - dprintk(KERN_INFO "%s: ViterbiStandard = ", __func__); - - switch (demod) { - case STV0900_DEMOD_1: - default: - fecmReg = R0900_P1_FECM; - prvitReg = R0900_P1_PRVIT; - break; - case STV0900_DEMOD_2: - fecmReg = R0900_P2_FECM; - prvitReg = R0900_P2_PRVIT; - break; - } - - switch (Standard) { + switch (standard) { case STV0900_AUTO_SEARCH: dprintk("Auto\n"); - stv0900_write_reg(i_params, fecmReg, 0x10); - stv0900_write_reg(i_params, prvitReg, 0x3F); + stv0900_write_reg(intp, FECM, 0x10); + stv0900_write_reg(intp, PRVIT, 0x3f); break; case STV0900_SEARCH_DVBS1: dprintk("DVBS1\n"); - stv0900_write_reg(i_params, fecmReg, 0x00); - switch (PunctureRate) { + stv0900_write_reg(intp, FECM, 0x00); + switch (fec) { case STV0900_FEC_UNKNOWN: default: - stv0900_write_reg(i_params, prvitReg, 0x2F); + stv0900_write_reg(intp, PRVIT, 0x2f); break; case STV0900_FEC_1_2: - stv0900_write_reg(i_params, prvitReg, 0x01); + stv0900_write_reg(intp, PRVIT, 0x01); break; case STV0900_FEC_2_3: - stv0900_write_reg(i_params, prvitReg, 0x02); + stv0900_write_reg(intp, PRVIT, 0x02); break; case STV0900_FEC_3_4: - stv0900_write_reg(i_params, prvitReg, 0x04); + stv0900_write_reg(intp, PRVIT, 0x04); break; case STV0900_FEC_5_6: - stv0900_write_reg(i_params, prvitReg, 0x08); + stv0900_write_reg(intp, PRVIT, 0x08); break; case STV0900_FEC_7_8: - stv0900_write_reg(i_params, prvitReg, 0x20); + stv0900_write_reg(intp, PRVIT, 0x20); break; } break; case STV0900_SEARCH_DSS: dprintk("DSS\n"); - stv0900_write_reg(i_params, fecmReg, 0x80); - switch (PunctureRate) { + stv0900_write_reg(intp, FECM, 0x80); + switch (fec) { case STV0900_FEC_UNKNOWN: default: - stv0900_write_reg(i_params, prvitReg, 0x13); + stv0900_write_reg(intp, PRVIT, 0x13); break; case STV0900_FEC_1_2: - stv0900_write_reg(i_params, prvitReg, 0x01); + stv0900_write_reg(intp, PRVIT, 0x01); break; case STV0900_FEC_2_3: - stv0900_write_reg(i_params, prvitReg, 0x02); + stv0900_write_reg(intp, PRVIT, 0x02); break; case STV0900_FEC_6_7: - stv0900_write_reg(i_params, prvitReg, 0x10); + stv0900_write_reg(intp, PRVIT, 0x10); break; } break; @@ -1043,340 +757,277 @@ static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params, } } -static void stv0900_track_optimization(struct dvb_frontend *fe) +static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod) { - struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; - enum fe_stv0900_demod_num demod = state->demod; + enum fe_stv0900_fec prate; + s32 rate_fld = stv0900_get_bits(intp, VIT_CURPUN); - s32 srate, pilots, aclc, freq1, freq0, - i = 0, timed, timef, blindTunSw = 0; + switch (rate_fld) { + case 13: + prate = STV0900_FEC_1_2; + break; + case 18: + prate = STV0900_FEC_2_3; + break; + case 21: + prate = STV0900_FEC_3_4; + break; + case 24: + prate = STV0900_FEC_5_6; + break; + case 25: + prate = STV0900_FEC_6_7; + break; + case 26: + prate = STV0900_FEC_7_8; + break; + default: + prate = STV0900_FEC_UNKNOWN; + break; + } - enum fe_stv0900_rolloff rolloff; - enum fe_stv0900_modcode foundModcod; + return prate; +} - dprintk(KERN_INFO "%s\n", __func__); +void stv0900_set_dvbs1_track_car_loop(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, + u32 srate) +{ + if (intp->chip_id >= 0x30) { + if (srate >= 15000000) { + stv0900_write_reg(intp, ACLC, 0x2b); + stv0900_write_reg(intp, BCLC, 0x1a); + } else if ((srate >= 7000000) && (15000000 > srate)) { + stv0900_write_reg(intp, ACLC, 0x0c); + stv0900_write_reg(intp, BCLC, 0x1b); + } else if (srate < 7000000) { + stv0900_write_reg(intp, ACLC, 0x2c); + stv0900_write_reg(intp, BCLC, 0x1c); + } - srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate += stv0900_get_timing_offst(i_params, srate, demod); + } else { /*cut 2.0 and 1.x*/ + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + } - switch (demod) { - case STV0900_DEMOD_1: - default: - switch (i_params->dmd1_rslts.standard) { - case STV0900_DVBS1_STANDARD: - if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) { - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - } +} - stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff); - stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75); - break; - case STV0900_DSS_STANDARD: - if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) { - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - } +static void stv0900_track_optimization(struct dvb_frontend *fe) +{ + struct stv0900_state *state = fe->demodulator_priv; + struct stv0900_internal *intp = state->internal; + enum fe_stv0900_demod_num demod = state->demod; - stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff); - stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75); - break; - case STV0900_DVBS2_STANDARD: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); - stv0900_write_reg(i_params, R0900_P1_ACLC, 0); - stv0900_write_reg(i_params, R0900_P1_BCLC, 0); - if (i_params->dmd1_rslts.frame_length == STV0900_LONG_FRAME) { - foundModcod = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD); - pilots = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01; - aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id); - if (foundModcod <= STV0900_QPSK_910) - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc); - else if (foundModcod <= STV0900_8PSK_910) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc); - } + s32 srate, + pilots, + aclc, + freq1, + freq0, + i = 0, + timed, + timef, + blind_tun_sw = 0, + modulation; - if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) { - if (foundModcod <= STV0900_16APSK_910) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc); - } else if (foundModcod <= STV0900_32APSK_910) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc); - } - } + enum fe_stv0900_rolloff rolloff; + enum fe_stv0900_modcode foundModcod; - } else { - aclc = stv0900_get_optim_short_carr_loop(srate, i_params->dmd1_rslts.modulation, i_params->chip_id); - if (i_params->dmd1_rslts.modulation == STV0900_QPSK) - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc); - - else if (i_params->dmd1_rslts.modulation == STV0900_8PSK) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc); - } else if (i_params->dmd1_rslts.modulation == STV0900_16APSK) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc); - } else if (i_params->dmd1_rslts.modulation == STV0900_32APSK) { - stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc); - } + dprintk("%s\n", __func__); - } + srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + srate += stv0900_get_timing_offst(intp, srate, demod); - if (i_params->chip_id <= 0x11) { - if (i_params->demod_mode != STV0900_SINGLE) - stv0900_activate_s2_modcode(i_params, demod); + switch (intp->result[demod].standard) { + case STV0900_DVBS1_STANDARD: + case STV0900_DSS_STANDARD: + dprintk("%s: found DVB-S or DSS\n", __func__); + if (intp->srch_standard[demod] == STV0900_AUTO_SEARCH) { + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 0); + } - } + stv0900_write_bits(intp, ROLLOFF_CONTROL, intp->rolloff); + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67); - break; - case STV0900_UNKNOWN_STANDARD: - default: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); + if (intp->chip_id < 0x30) { + stv0900_write_reg(intp, ERRCTRL1, 0x75); break; } - freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2); - freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1); - rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS); - if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) { - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00); - stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01); - stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod); - stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod); - stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod); - blindTunSw = 1; + if (stv0900_get_vit_fec(intp, demod) == STV0900_FEC_1_2) { + stv0900_write_reg(intp, GAUSSR0, 0x98); + stv0900_write_reg(intp, CCIR0, 0x18); + } else { + stv0900_write_reg(intp, GAUSSR0, 0x18); + stv0900_write_reg(intp, CCIR0, 0x18); } - if (i_params->chip_id >= 0x20) { - if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) { - stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0a); - stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x0); + stv0900_write_reg(intp, ERRCTRL1, 0x75); + break; + case STV0900_DVBS2_STANDARD: + dprintk("%s: found DVB-S2\n", __func__); + stv0900_write_bits(intp, DVBS1_ENABLE, 0); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_reg(intp, ACLC, 0); + stv0900_write_reg(intp, BCLC, 0); + if (intp->result[demod].frame_len == STV0900_LONG_FRAME) { + foundModcod = stv0900_get_bits(intp, DEMOD_MODCOD); + pilots = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; + aclc = stv0900_get_optim_carr_loop(srate, + foundModcod, + pilots, + intp->chip_id); + if (foundModcod <= STV0900_QPSK_910) + stv0900_write_reg(intp, ACLC2S2Q, aclc); + else if (foundModcod <= STV0900_8PSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S28, aclc); } - } - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x08); - - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0x0A); - - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38); - - if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd1_symbol_rate < 10000000)) { - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - i_params->tuner1_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000; - if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) { - if (i_params->dmd1_srch_algo != STV0900_WARM_START) - stv0900_set_bandwidth(fe, i_params->tuner1_bw); + if ((intp->demod_mode == STV0900_SINGLE) && + (foundModcod > STV0900_8PSK_910)) { + if (foundModcod <= STV0900_16APSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S216A, + aclc); + } else if (foundModcod <= STV0900_32APSK_910) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S232A, + aclc); + } } - if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000)) - msleep(50); - else - msleep(5); - - stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START); - - if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) { - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - i = 0; - while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) { - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - i++; - } + } else { + modulation = intp->result[demod].modulation; + aclc = stv0900_get_optim_short_carr_loop(srate, + modulation, intp->chip_id); + if (modulation == STV0900_QPSK) + stv0900_write_reg(intp, ACLC2S2Q, aclc); + else if (modulation == STV0900_8PSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S28, aclc); + } else if (modulation == STV0900_16APSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S216A, aclc); + } else if (modulation == STV0900_32APSK) { + stv0900_write_reg(intp, ACLC2S2Q, 0x2a); + stv0900_write_reg(intp, ACLC2S232A, aclc); } } - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49); + if (intp->chip_id <= 0x11) { + if (intp->demod_mode != STV0900_SINGLE) + stv0900_activate_s2_modcod(intp, demod); - if ((i_params->dmd1_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd1_rslts.standard == STV0900_DSS_STANDARD)) - stv0900_set_viterbi_tracq(i_params, demod); + } + stv0900_write_reg(intp, ERRCTRL1, 0x67); break; + case STV0900_UNKNOWN_STANDARD: + default: + dprintk("%s: found unknown standard\n", __func__); + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + break; + } - case STV0900_DEMOD_2: - switch (i_params->dmd2_rslts.standard) { - case STV0900_DVBS1_STANDARD: - - if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) { - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - } + freq1 = stv0900_read_reg(intp, CFR2); + freq0 = stv0900_read_reg(intp, CFR1); + rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { + stv0900_write_reg(intp, SFRSTEP, 0x00); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, TMGCFG2, 0xc1); + stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); + blind_tun_sw = 1; + if (intp->result[demod].standard != STV0900_DVBS2_STANDARD) + stv0900_set_dvbs1_track_car_loop(intp, demod, srate); - stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff); - stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75); - break; - case STV0900_DSS_STANDARD: - if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) { - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - } + } - stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff); - stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1); - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75); - break; - case STV0900_DVBS2_STANDARD: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - stv0900_write_reg(i_params, R0900_P2_ACLC, 0); - stv0900_write_reg(i_params, R0900_P2_BCLC, 0); - if (i_params->dmd2_rslts.frame_length == STV0900_LONG_FRAME) { - foundModcod = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD); - pilots = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01; - aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id); - if (foundModcod <= STV0900_QPSK_910) - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc); - else if (foundModcod <= STV0900_8PSK_910) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc); - } + if (intp->chip_id >= 0x20) { + if ((intp->srch_standard[demod] == STV0900_SEARCH_DVBS1) || + (intp->srch_standard[demod] == + STV0900_SEARCH_DSS) || + (intp->srch_standard[demod] == + STV0900_AUTO_SEARCH)) { + stv0900_write_reg(intp, VAVSRVIT, 0x0a); + stv0900_write_reg(intp, VITSCALE, 0x0); + } + } - if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) { - if (foundModcod <= STV0900_16APSK_910) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc); - } else if (foundModcod <= STV0900_32APSK_910) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc); - } + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x08); - } + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0x0a); - } else { - aclc = stv0900_get_optim_short_carr_loop(srate, - i_params->dmd2_rslts.modulation, - i_params->chip_id); - - if (i_params->dmd2_rslts.modulation == STV0900_QPSK) - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc); - - else if (i_params->dmd2_rslts.modulation == STV0900_8PSK) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc); - } else if (i_params->dmd2_rslts.modulation == STV0900_16APSK) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc); - } else if (i_params->dmd2_rslts.modulation == STV0900_32APSK) { - stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a); - stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc); - } - } + stv0900_write_reg(intp, AGC2REF, 0x38); - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67); + if ((intp->chip_id >= 0x20) || + (blind_tun_sw == 1) || + (intp->symbol_rate[demod] < 10000000)) { + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + intp->bw[demod] = stv0900_carrier_width(srate, + intp->rolloff) + 10000000; - break; - case STV0900_UNKNOWN_STANDARD: - default: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - break; + if ((intp->chip_id >= 0x20) || (blind_tun_sw == 1)) { + if (intp->srch_algo[demod] != STV0900_WARM_START) + stv0900_set_bandwidth(fe, intp->bw[demod]); } - freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2); - freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1); - rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS); - if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) { - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00); - stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01); - stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod); - stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod); - stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod); - blindTunSw = 1; - } + if ((intp->srch_algo[demod] == STV0900_BLIND_SEARCH) || + (intp->symbol_rate[demod] < 10000000)) + msleep(50); + else + msleep(5); - if (i_params->chip_id >= 0x20) { - if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) { - stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0a); - stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x0); + stv0900_get_lock_timeout(&timed, &timef, srate, + STV0900_WARM_START); + + if (stv0900_get_demod_lock(intp, demod, timed / 2) == FALSE) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + i = 0; + while ((stv0900_get_demod_lock(intp, + demod, + timed / 2) == FALSE) && + (i <= 2)) { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + i++; } } - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x08); + } - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0x0a); + if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x49); - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38); - if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd2_symbol_rate < 10000000)) { - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000; + if ((intp->result[demod].standard == STV0900_DVBS1_STANDARD) || + (intp->result[demod].standard == STV0900_DSS_STANDARD)) + stv0900_set_viterbi_tracq(intp, demod); - if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) { - if (i_params->dmd2_srch_algo != STV0900_WARM_START) - stv0900_set_bandwidth(fe, i_params->tuner2_bw); - } - - if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000)) - msleep(50); - else - msleep(5); - - stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START); - if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) { - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - i = 0; - while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) { - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - i++; - } - } - } - - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49); - - if ((i_params->dmd2_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd2_rslts.standard == STV0900_DSS_STANDARD)) - stv0900_set_viterbi_tracq(i_params, demod); - - break; - } } -static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv0900_demod_num demod, s32 time_out) +static int stv0900_get_fec_lock(struct stv0900_internal *intp, + enum fe_stv0900_demod_num demod, s32 time_out) { - s32 timer = 0, lock = 0, header_field, pktdelin_field, lock_vit_field; + s32 timer = 0, lock = 0; enum fe_stv0900_search_state dmd_state; - dprintk(KERN_INFO "%s\n", __func__); - - dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(pktdelin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK); - dmd_reg(lock_vit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT); + dprintk("%s\n", __func__); - dmd_state = stv0900_get_bits(i_params, header_field); + dmd_state = stv0900_get_bits(intp, HEADER_MODE); while ((timer < time_out) && (lock == 0)) { switch (dmd_state) { @@ -1386,10 +1037,10 @@ static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv09 lock = 0; break; case STV0900_DVBS2_FOUND: - lock = stv0900_get_bits(i_params, pktdelin_field); + lock = stv0900_get_bits(intp, PKTDELIN_LOCK); break; case STV0900_DVBS_FOUND: - lock = stv0900_get_bits(i_params, lock_vit_field); + lock = stv0900_get_bits(intp, LOCKEDVIT); break; } @@ -1400,46 +1051,44 @@ static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv09 } if (lock) - dprintk("DEMOD FEC LOCK OK\n"); + dprintk("%s: DEMOD FEC LOCK OK\n", __func__); else - dprintk("DEMOD FEC LOCK FAIL\n"); + dprintk("%s: DEMOD FEC LOCK FAIL\n", __func__); return lock; } -static int stv0900_wait_for_lock(struct stv0900_internal *i_params, +static int stv0900_wait_for_lock(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod, s32 dmd_timeout, s32 fec_timeout) { - s32 timer = 0, lock = 0, str_merg_rst_fld, str_merg_lock_fld; + s32 timer = 0, lock = 0; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - dmd_reg(str_merg_rst_fld, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE); - dmd_reg(str_merg_lock_fld, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK); - - lock = stv0900_get_demod_lock(i_params, demod, dmd_timeout); + lock = stv0900_get_demod_lock(intp, demod, dmd_timeout); if (lock) - lock = lock && stv0900_get_fec_lock(i_params, demod, fec_timeout); + lock = lock && stv0900_get_fec_lock(intp, demod, fec_timeout); if (lock) { lock = 0; - dprintk(KERN_INFO "%s: Timer = %d, time_out = %d\n", __func__, timer, fec_timeout); + dprintk("%s: Timer = %d, time_out = %d\n", + __func__, timer, fec_timeout); while ((timer < fec_timeout) && (lock == 0)) { - lock = stv0900_get_bits(i_params, str_merg_lock_fld); + lock = stv0900_get_bits(intp, TSFIFO_LINEOK); msleep(1); timer++; } } if (lock) - dprintk(KERN_INFO "%s: DEMOD LOCK OK\n", __func__); + dprintk("%s: DEMOD LOCK OK\n", __func__); else - dprintk(KERN_INFO "%s: DEMOD LOCK FAIL\n", __func__); + dprintk("%s: DEMOD LOCK FAIL\n", __func__); if (lock) return TRUE; @@ -1451,43 +1100,43 @@ enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe, enum fe_stv0900_demod_num demod) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_tracking_standard fnd_standard; - s32 state_field, - dss_dvb_field; - - dprintk(KERN_INFO "%s\n", __func__); - dmd_reg(state_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE); - dmd_reg(dss_dvb_field, F0900_P1_DSS_DVB, F0900_P2_DSS_DVB); + int hdr_mode = stv0900_get_bits(intp, HEADER_MODE); - if (stv0900_get_bits(i_params, state_field) == 2) + switch (hdr_mode) { + case 2: fnd_standard = STV0900_DVBS2_STANDARD; - - else if (stv0900_get_bits(i_params, state_field) == 3) { - if (stv0900_get_bits(i_params, dss_dvb_field) == 1) + break; + case 3: + if (stv0900_get_bits(intp, DSS_DVB) == 1) fnd_standard = STV0900_DSS_STANDARD; else fnd_standard = STV0900_DVBS1_STANDARD; - } else + + break; + default: fnd_standard = STV0900_UNKNOWN_STANDARD; + } + + dprintk("%s: standard %d\n", __func__, fnd_standard); return fnd_standard; } -static s32 stv0900_get_carr_freq(struct stv0900_internal *i_params, u32 mclk, +static s32 stv0900_get_carr_freq(struct stv0900_internal *intp, u32 mclk, enum fe_stv0900_demod_num demod) { - s32 cfr_field2, cfr_field1, cfr_field0, - derot, rem1, rem2, intval1, intval2; + s32 derot, + rem1, + rem2, + intval1, + intval2; - dmd_reg(cfr_field2, F0900_P1_CAR_FREQ2, F0900_P2_CAR_FREQ2); - dmd_reg(cfr_field1, F0900_P1_CAR_FREQ1, F0900_P2_CAR_FREQ1); - dmd_reg(cfr_field0, F0900_P1_CAR_FREQ0, F0900_P2_CAR_FREQ0); - - derot = (stv0900_get_bits(i_params, cfr_field2) << 16) + - (stv0900_get_bits(i_params, cfr_field1) << 8) + - (stv0900_get_bits(i_params, cfr_field0)); + derot = (stv0900_get_bits(intp, CAR_FREQ2) << 16) + + (stv0900_get_bits(intp, CAR_FREQ1) << 8) + + (stv0900_get_bits(intp, CAR_FREQ0)); derot = ge2comp(derot, 24); intval1 = mclk >> 12; @@ -1505,7 +1154,7 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) { struct dvb_frontend_ops *frontend_ops = NULL; struct dvb_tuner_ops *tuner_ops = NULL; - u32 frequency = 0; + u32 freq = 0; if (&fe->ops) frontend_ops = &fe->ops; @@ -1514,304 +1163,159 @@ static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe) tuner_ops = &frontend_ops->tuner_ops; if (tuner_ops->get_frequency) { - if ((tuner_ops->get_frequency(fe, &frequency)) < 0) + if ((tuner_ops->get_frequency(fe, &freq)) < 0) dprintk("%s: Invalid parameter\n", __func__); else - dprintk("%s: Frequency=%d\n", __func__, frequency); - - } + dprintk("%s: Frequency=%d\n", __func__, freq); - return frequency; -} - -static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *i_params, - enum fe_stv0900_demod_num demod) -{ - s32 rate_fld, vit_curpun_fld; - enum fe_stv0900_fec prate; - - dmd_reg(vit_curpun_fld, F0900_P1_VIT_CURPUN, F0900_P2_VIT_CURPUN); - rate_fld = stv0900_get_bits(i_params, vit_curpun_fld); - - switch (rate_fld) { - case 13: - prate = STV0900_FEC_1_2; - break; - case 18: - prate = STV0900_FEC_2_3; - break; - case 21: - prate = STV0900_FEC_3_4; - break; - case 24: - prate = STV0900_FEC_5_6; - break; - case 25: - prate = STV0900_FEC_6_7; - break; - case 26: - prate = STV0900_FEC_7_8; - break; - default: - prate = STV0900_FEC_UNKNOWN; - break; } - return prate; + return freq; } -static enum fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) +static enum +fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; enum fe_stv0900_signal_type range = STV0900_OUTOFRANGE; - s32 offsetFreq, - srate_offset, - i = 0; + struct stv0900_signal_info *result = &intp->result[demod]; + s32 offsetFreq, + srate_offset; + int i = 0, + d = demod; u8 timing; msleep(5); - switch (demod) { - case STV0900_DEMOD_1: - default: - if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) { - timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2); - i = 0; - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x5c); - - while ((i <= 50) && (timing != 0) && (timing != 0xFF)) { - timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2); - msleep(5); - i += 5; - } - } - - i_params->dmd1_rslts.standard = stv0900_get_standard(fe, demod); - i_params->dmd1_rslts.frequency = stv0900_get_tuner_freq(fe); - offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000; - i_params->dmd1_rslts.frequency += offsetFreq; - i_params->dmd1_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd1_rslts.symbol_rate, demod); - i_params->dmd1_rslts.symbol_rate += srate_offset; - i_params->dmd1_rslts.fec = stv0900_get_vit_fec(i_params, demod); - i_params->dmd1_rslts.modcode = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD); - i_params->dmd1_rslts.pilot = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01; - i_params->dmd1_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE)) >> 1; - i_params->dmd1_rslts.rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS); - switch (i_params->dmd1_rslts.standard) { - case STV0900_DVBS2_STANDARD: - i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_SPECINV_DEMOD); - if (i_params->dmd1_rslts.modcode <= STV0900_QPSK_910) - i_params->dmd1_rslts.modulation = STV0900_QPSK; - else if (i_params->dmd1_rslts.modcode <= STV0900_8PSK_910) - i_params->dmd1_rslts.modulation = STV0900_8PSK; - else if (i_params->dmd1_rslts.modcode <= STV0900_16APSK_910) - i_params->dmd1_rslts.modulation = STV0900_16APSK; - else if (i_params->dmd1_rslts.modcode <= STV0900_32APSK_910) - i_params->dmd1_rslts.modulation = STV0900_32APSK; - else - i_params->dmd1_rslts.modulation = STV0900_UNKNOWN; - break; - case STV0900_DVBS1_STANDARD: - case STV0900_DSS_STANDARD: - i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_IQINV); - i_params->dmd1_rslts.modulation = STV0900_QPSK; - break; - default: - break; + if (intp->srch_algo[d] == STV0900_BLIND_SEARCH) { + timing = stv0900_read_reg(intp, TMGREG2); + i = 0; + stv0900_write_reg(intp, SFRSTEP, 0x5c); + + while ((i <= 50) && (timing != 0) && (timing != 0xff)) { + timing = stv0900_read_reg(intp, TMGREG2); + msleep(5); + i += 5; } + } - if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000)) { - offsetFreq = i_params->dmd1_rslts.frequency - i_params->tuner1_freq; - i_params->tuner1_freq = stv0900_get_tuner_freq(fe); - if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500)) - range = STV0900_RANGEOK; - else - if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd1_rslts.symbol_rate, i_params->dmd1_rslts.rolloff) / 2000)) - range = STV0900_RANGEOK; - else - range = STV0900_OUTOFRANGE; - - } else { - if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500)) - range = STV0900_RANGEOK; - else - range = STV0900_OUTOFRANGE; - } + result->standard = stv0900_get_standard(fe, d); + result->frequency = stv0900_get_tuner_freq(fe); + offsetFreq = stv0900_get_carr_freq(intp, intp->mclk, d) / 1000; + result->frequency += offsetFreq; + result->symbol_rate = stv0900_get_symbol_rate(intp, intp->mclk, d); + srate_offset = stv0900_get_timing_offst(intp, result->symbol_rate, d); + result->symbol_rate += srate_offset; + result->fec = stv0900_get_vit_fec(intp, d); + result->modcode = stv0900_get_bits(intp, DEMOD_MODCOD); + result->pilot = stv0900_get_bits(intp, DEMOD_TYPE) & 0x01; + result->frame_len = ((u32)stv0900_get_bits(intp, DEMOD_TYPE)) >> 1; + result->rolloff = stv0900_get_bits(intp, ROLLOFF_STATUS); + switch (result->standard) { + case STV0900_DVBS2_STANDARD: + result->spectrum = stv0900_get_bits(intp, SPECINV_DEMOD); + if (result->modcode <= STV0900_QPSK_910) + result->modulation = STV0900_QPSK; + else if (result->modcode <= STV0900_8PSK_910) + result->modulation = STV0900_8PSK; + else if (result->modcode <= STV0900_16APSK_910) + result->modulation = STV0900_16APSK; + else if (result->modcode <= STV0900_32APSK_910) + result->modulation = STV0900_32APSK; + else + result->modulation = STV0900_UNKNOWN; break; - case STV0900_DEMOD_2: - if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) { - timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2); - i = 0; - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x5c); - - while ((i <= 50) && (timing != 0) && (timing != 0xff)) { - timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2); - msleep(5); - i += 5; - } - } - - i_params->dmd2_rslts.standard = stv0900_get_standard(fe, demod); - i_params->dmd2_rslts.frequency = stv0900_get_tuner_freq(fe); - offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000; - i_params->dmd2_rslts.frequency += offsetFreq; - i_params->dmd2_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd2_rslts.symbol_rate, demod); - i_params->dmd2_rslts.symbol_rate += srate_offset; - i_params->dmd2_rslts.fec = stv0900_get_vit_fec(i_params, demod); - i_params->dmd2_rslts.modcode = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD); - i_params->dmd2_rslts.pilot = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01; - i_params->dmd2_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE)) >> 1; - i_params->dmd2_rslts.rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS); - switch (i_params->dmd2_rslts.standard) { - case STV0900_DVBS2_STANDARD: - i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_SPECINV_DEMOD); - if (i_params->dmd2_rslts.modcode <= STV0900_QPSK_910) - i_params->dmd2_rslts.modulation = STV0900_QPSK; - else if (i_params->dmd2_rslts.modcode <= STV0900_8PSK_910) - i_params->dmd2_rslts.modulation = STV0900_8PSK; - else if (i_params->dmd2_rslts.modcode <= STV0900_16APSK_910) - i_params->dmd2_rslts.modulation = STV0900_16APSK; - else if (i_params->dmd2_rslts.modcode <= STV0900_32APSK_910) - i_params->dmd2_rslts.modulation = STV0900_32APSK; - else - i_params->dmd2_rslts.modulation = STV0900_UNKNOWN; - break; - case STV0900_DVBS1_STANDARD: - case STV0900_DSS_STANDARD: - i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_IQINV); - i_params->dmd2_rslts.modulation = STV0900_QPSK; - break; - default: - break; - } + case STV0900_DVBS1_STANDARD: + case STV0900_DSS_STANDARD: + result->spectrum = stv0900_get_bits(intp, IQINV); + result->modulation = STV0900_QPSK; + break; + default: + break; + } - if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000)) { - offsetFreq = i_params->dmd2_rslts.frequency - i_params->tuner2_freq; - i_params->tuner2_freq = stv0900_get_tuner_freq(fe); + if ((intp->srch_algo[d] == STV0900_BLIND_SEARCH) || + (intp->symbol_rate[d] < 10000000)) { + offsetFreq = result->frequency - intp->freq[d]; + intp->freq[d] = stv0900_get_tuner_freq(fe); + if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) + range = STV0900_RANGEOK; + else if (ABS(offsetFreq) <= + (stv0900_carrier_width(result->symbol_rate, + result->rolloff) / 2000)) + range = STV0900_RANGEOK; - if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500)) - range = STV0900_RANGEOK; - else - if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd2_rslts.symbol_rate, i_params->dmd2_rslts.rolloff) / 2000)) - range = STV0900_RANGEOK; - else - range = STV0900_OUTOFRANGE; - } else { - if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500)) - range = STV0900_RANGEOK; - else - range = STV0900_OUTOFRANGE; - } + } else if (ABS(offsetFreq) <= ((intp->srch_range[d] / 2000) + 500)) + range = STV0900_RANGEOK; - break; - } + dprintk("%s: range %d\n", __func__, range); return range; } -static enum fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe) +static enum +fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - - s32 srate, demod_timeout, - fec_timeout, freq1, freq0; enum fe_stv0900_signal_type signal_type = STV0900_NODATA; - switch (demod) { - case STV0900_DEMOD_1: - default: - i_params->dmd1_rslts.locked = FALSE; - if (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) { - srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate += stv0900_get_timing_offst(i_params, srate, demod); - if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) - stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod); - - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START); - freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2); - freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) { - i_params->dmd1_rslts.locked = TRUE; - signal_type = stv0900_get_signal_params(fe); - stv0900_track_optimization(fe); - } else { - stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1c); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18); - if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) { - i_params->dmd1_rslts.locked = TRUE; - signal_type = stv0900_get_signal_params(fe); - stv0900_track_optimization(fe); - } - - } - - } else - i_params->dmd1_rslts.locked = FALSE; - - break; - case STV0900_DEMOD_2: - i_params->dmd2_rslts.locked = FALSE; - if (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) { - srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - srate += stv0900_get_timing_offst(i_params, srate, demod); - - if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) - stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod); - - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START); - freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2); - freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - - if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) { - i_params->dmd2_rslts.locked = TRUE; + s32 srate, + demod_timeout, + fec_timeout, + freq1, + freq0; + + intp->result[demod].locked = FALSE; + + if (stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) { + srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + srate += stv0900_get_timing_offst(intp, srate, demod); + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) + stv0900_set_symbol_rate(intp, intp->mclk, srate, demod); + + stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, + srate, STV0900_WARM_START); + freq1 = stv0900_read_reg(intp, CFR2); + freq0 = stv0900_read_reg(intp, CFR1); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_bits(intp, SPECINV_CONTROL, + STV0900_IQ_FORCE_SWAPPED); + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + if (stv0900_wait_for_lock(intp, demod, + demod_timeout, fec_timeout) == TRUE) { + intp->result[demod].locked = TRUE; + signal_type = stv0900_get_signal_params(fe); + stv0900_track_optimization(fe); + } else { + stv0900_write_bits(intp, SPECINV_CONTROL, + STV0900_IQ_FORCE_NORMAL); + stv0900_write_reg(intp, DMDISTATE, 0x1c); + stv0900_write_reg(intp, CFRINIT1, freq1); + stv0900_write_reg(intp, CFRINIT0, freq0); + stv0900_write_reg(intp, DMDISTATE, 0x18); + if (stv0900_wait_for_lock(intp, demod, + demod_timeout, fec_timeout) == TRUE) { + intp->result[demod].locked = TRUE; signal_type = stv0900_get_signal_params(fe); stv0900_track_optimization(fe); - } else { - stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1c); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18); - - if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) { - i_params->dmd2_rslts.locked = TRUE; - signal_type = stv0900_get_signal_params(fe); - stv0900_track_optimization(fe); - } - } - } else - i_params->dmd1_rslts.locked = FALSE; + } - break; - } + } else + intp->result[demod].locked = FALSE; return signal_type; } -static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params, +static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { u32 minagc2level = 0xffff, @@ -1820,105 +1324,54 @@ static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params, s32 i, j, nb_steps, direction; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38); - stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1); + stv0900_write_reg(intp, AGC2REF, 0x38); + stv0900_write_bits(intp, SCAN_ENABLE, 0); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); - stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83); - stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0); + stv0900_write_bits(intp, AUTO_GUP, 1); + stv0900_write_bits(intp, AUTO_GLOW, 1); - stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82); - stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0); - stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0); + stv0900_write_reg(intp, DMDT0M, 0x0); - stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod); - nb_steps = -1 + (i_params->dmd1_srch_range / 1000000); - nb_steps /= 2; - nb_steps = (2 * nb_steps) + 1; + stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); + nb_steps = -1 + (intp->srch_range[demod] / 1000000); + nb_steps /= 2; + nb_steps = (2 * nb_steps) + 1; - if (nb_steps < 0) - nb_steps = 1; + if (nb_steps < 0) + nb_steps = 1; - direction = 1; + direction = 1; - freq_step = (1000000 << 8) / (i_params->mclk >> 8); + freq_step = (1000000 << 8) / (intp->mclk >> 8); - init_freq = 0; + init_freq = 0; - for (i = 0; i < nb_steps; i++) { - if (direction > 0) - init_freq = init_freq + (freq_step * i); - else - init_freq = init_freq - (freq_step * i); - - direction *= -1; - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (init_freq >> 8) & 0xff); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, init_freq & 0xff); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x58); - msleep(10); - agc2level = 0; - - for (j = 0; j < 10; j++) - agc2level += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P1_AGC2I0); - - agc2level /= 10; - - if (agc2level < minagc2level) - minagc2level = agc2level; - } - break; - case STV0900_DEMOD_2: - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38); - stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1); - stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83); - stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0); - stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82); - stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0); - stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0); - stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod); - nb_steps = -1 + (i_params->dmd2_srch_range / 1000000); - nb_steps /= 2; - nb_steps = (2 * nb_steps) + 1; - - if (nb_steps < 0) - nb_steps = 1; - - direction = 1; - freq_step = (1000000 << 8) / (i_params->mclk >> 8); - init_freq = 0; - for (i = 0; i < nb_steps; i++) { - if (direction > 0) - init_freq = init_freq + (freq_step * i); - else - init_freq = init_freq - (freq_step * i); + for (i = 0; i < nb_steps; i++) { + if (direction > 0) + init_freq = init_freq + (freq_step * i); + else + init_freq = init_freq - (freq_step * i); - direction *= -1; + direction *= -1; + stv0900_write_reg(intp, DMDISTATE, 0x5C); + stv0900_write_reg(intp, CFRINIT1, (init_freq >> 8) & 0xff); + stv0900_write_reg(intp, CFRINIT0, init_freq & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x58); + msleep(10); + agc2level = 0; - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (init_freq >> 8) & 0xff); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, init_freq & 0xff); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x58); + for (j = 0; j < 10; j++) + agc2level += (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); - msleep(10); - agc2level = 0; - for (j = 0; j < 10; j++) - agc2level += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P2_AGC2I0); + agc2level /= 10; - agc2level /= 10; + if (agc2level < minagc2level) + minagc2level = agc2level; - if (agc2level < minagc2level) - minagc2level = agc2level; - } - break; } return (u16)minagc2level; @@ -1927,336 +1380,192 @@ static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params, static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - int timingLock = FALSE; + int timing_lck = FALSE; s32 i, timingcpt = 0, direction = 1, nb_steps, current_step = 0, tuner_freq; + u32 agc2_th, + coarse_srate = 0, + agc2_integr = 0, + currier_step = 1200; - u32 coarse_srate = 0, agc2_integr = 0, currier_step = 1200; - - switch (demod) { - case STV0900_DEMOD_1: - default: - stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0x12); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xf0); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xe0); - stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1); - stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83); - stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0); - stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82); - stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0); - stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0); - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x50); - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x6a); - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x95); - } else { - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed); - stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x73); - } + if (intp->chip_id >= 0x30) + agc2_th = 0x2e00; + else + agc2_th = 0x1f00; + + stv0900_write_bits(intp, DEMOD_MODE, 0x1f); + stv0900_write_reg(intp, TMGCFG, 0x12); + stv0900_write_reg(intp, TMGTHRISE, 0xf0); + stv0900_write_reg(intp, TMGTHFALL, 0xe0); + stv0900_write_bits(intp, SCAN_ENABLE, 1); + stv0900_write_bits(intp, CFR_AUTOSCAN, 1); + stv0900_write_reg(intp, SFRUP1, 0x83); + stv0900_write_reg(intp, SFRUP0, 0xc0); + stv0900_write_reg(intp, SFRLOW1, 0x82); + stv0900_write_reg(intp, SFRLOW0, 0xa0); + stv0900_write_reg(intp, DMDT0M, 0x0); + stv0900_write_reg(intp, AGC2REF, 0x50); + + if (intp->chip_id >= 0x30) { + stv0900_write_reg(intp, CARFREQ, 0x99); + stv0900_write_reg(intp, SFRSTEP, 0x98); + } else if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, CARFREQ, 0x6a); + stv0900_write_reg(intp, SFRSTEP, 0x95); + } else { + stv0900_write_reg(intp, CARFREQ, 0xed); + stv0900_write_reg(intp, SFRSTEP, 0x73); + } - if (i_params->dmd1_symbol_rate <= 2000000) - currier_step = 1000; - else if (i_params->dmd1_symbol_rate <= 5000000) - currier_step = 2000; - else if (i_params->dmd1_symbol_rate <= 12000000) - currier_step = 3000; - else + if (intp->symbol_rate[demod] <= 2000000) + currier_step = 1000; + else if (intp->symbol_rate[demod] <= 5000000) + currier_step = 2000; + else if (intp->symbol_rate[demod] <= 12000000) + currier_step = 3000; + else currier_step = 5000; - nb_steps = -1 + ((i_params->dmd1_srch_range / 1000) / currier_step); - nb_steps /= 2; - nb_steps = (2 * nb_steps) + 1; - - if (nb_steps < 0) - nb_steps = 1; - - else if (nb_steps > 10) { - nb_steps = 11; - currier_step = (i_params->dmd1_srch_range / 1000) / 10; - } - - current_step = 0; + nb_steps = -1 + ((intp->srch_range[demod] / 1000) / currier_step); + nb_steps /= 2; + nb_steps = (2 * nb_steps) + 1; - direction = 1; - tuner_freq = i_params->tuner1_freq; - - while ((timingLock == FALSE) && (current_step < nb_steps)) { - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5F); - stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x0); - - msleep(50); - - for (i = 0; i < 10; i++) { - if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2) - timingcpt++; - - agc2_integr += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) | stv0900_read_reg(i_params, R0900_P1_AGC2I0); - - } - - agc2_integr /= 10; - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - current_step++; - direction *= -1; - - dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started. tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", tuner_freq, agc2_integr, coarse_srate, timingcpt); - - if ((timingcpt >= 5) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000)) { - timingLock = TRUE; - } - - else if (current_step < nb_steps) { - if (direction > 0) - tuner_freq += (current_step * currier_step); - else - tuner_freq -= (current_step * currier_step); + if (nb_steps < 0) + nb_steps = 1; + else if (nb_steps > 10) { + nb_steps = 11; + currier_step = (intp->srch_range[demod] / 1000) / 10; + } - stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw); - } - } + current_step = 0; + direction = 1; - if (timingLock == FALSE) - coarse_srate = 0; - else - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - break; - case STV0900_DEMOD_2: - stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0x12); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xf0); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xe0); - stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1); - stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83); - stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0); - stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82); - stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0); - stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0); - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x50); - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x6a); - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x95); - } else { - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed); - stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x73); - } + tuner_freq = intp->freq[demod]; - if (i_params->dmd2_symbol_rate <= 2000000) - currier_step = 1000; - else if (i_params->dmd2_symbol_rate <= 5000000) - currier_step = 2000; - else if (i_params->dmd2_symbol_rate <= 12000000) - currier_step = 3000; - else - currier_step = 5000; + while ((timing_lck == FALSE) && (current_step < nb_steps)) { + stv0900_write_reg(intp, DMDISTATE, 0x5f); + stv0900_write_bits(intp, DEMOD_MODE, 0); + msleep(50); - nb_steps = -1 + ((i_params->dmd2_srch_range / 1000) / currier_step); - nb_steps /= 2; - nb_steps = (2 * nb_steps) + 1; + for (i = 0; i < 10; i++) { + if (stv0900_get_bits(intp, TMGLOCK_QUALITY) >= 2) + timingcpt++; - if (nb_steps < 0) - nb_steps = 1; - else if (nb_steps > 10) { - nb_steps = 11; - currier_step = (i_params->dmd2_srch_range / 1000) / 10; + agc2_integr += (stv0900_read_reg(intp, AGC2I1) << 8) | + stv0900_read_reg(intp, AGC2I0); } - current_step = 0; - direction = 1; - tuner_freq = i_params->tuner2_freq; - - while ((timingLock == FALSE) && (current_step < nb_steps)) { - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5F); - stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x0); - - msleep(50); - timingcpt = 0; - - for (i = 0; i < 20; i++) { - if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2) - timingcpt++; - agc2_integr += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P2_AGC2I0); - } - - agc2_integr /= 20; - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - if ((timingcpt >= 10) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000)) - timingLock = TRUE; - else { - current_step++; - direction *= -1; - - if (direction > 0) - tuner_freq += (current_step * currier_step); - else - tuner_freq -= (current_step * currier_step); + agc2_integr /= 10; + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + current_step++; + direction *= -1; + + dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started." + " tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", + tuner_freq, agc2_integr, coarse_srate, timingcpt); + + if ((timingcpt >= 5) && + (agc2_integr < agc2_th) && + (coarse_srate < 55000000) && + (coarse_srate > 850000)) + timing_lck = TRUE; + else if (current_step < nb_steps) { + if (direction > 0) + tuner_freq += (current_step * currier_step); + else + tuner_freq -= (current_step * currier_step); - stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw); - } + stv0900_set_tuner(fe, tuner_freq, intp->bw[demod]); } - - if (timingLock == FALSE) - coarse_srate = 0; - else - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); - break; } + if (timing_lck == FALSE) + coarse_srate = 0; + else + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + return coarse_srate; } static u32 stv0900_search_srate_fine(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - u32 coarse_srate, - coarse_freq, - symb; + u32 coarse_srate, + coarse_freq, + symb, + symbmax, + symbmin, + symbcomp; + + coarse_srate = stv0900_get_symbol_rate(intp, intp->mclk, demod); + + if (coarse_srate > 3000000) { + symbmax = 13 * (coarse_srate / 10); + symbmax = (symbmax / 1000) * 65536; + symbmax /= (intp->mclk / 1000); + + symbmin = 10 * (coarse_srate / 13); + symbmin = (symbmin / 1000)*65536; + symbmin /= (intp->mclk / 1000); + + symb = (coarse_srate / 1000) * 65536; + symb /= (intp->mclk / 1000); + } else { + symbmax = 13 * (coarse_srate / 10); + symbmax = (symbmax / 100) * 65536; + symbmax /= (intp->mclk / 100); - coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod); + symbmin = 10 * (coarse_srate / 14); + symbmin = (symbmin / 100) * 65536; + symbmin /= (intp->mclk / 100); - switch (demod) { - case STV0900_DEMOD_1: - default: - coarse_freq = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8) - | stv0900_read_reg(i_params, R0900_P1_CFR1); - symb = 13 * (coarse_srate / 10); - - if (symb < i_params->dmd1_symbol_rate) - coarse_srate = 0; - else { - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01); - stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20); - stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x00); - stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2); - stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0); - - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49); - else - stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed); - - if (coarse_srate > 3000000) { - symb = 13 * (coarse_srate / 10); - symb = (symb / 1000) * 65536; - symb /= (i_params->mclk / 1000); - stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF)); - - symb = 10 * (coarse_srate / 13); - symb = (symb / 1000) * 65536; - symb /= (i_params->mclk / 1000); - - stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF)); - - symb = (coarse_srate / 1000) * 65536; - symb /= (i_params->mclk / 1000); - stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF)); - } else { - symb = 13 * (coarse_srate / 10); - symb = (symb / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF)); - - symb = 10 * (coarse_srate / 14); - symb = (symb / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF)); - - symb = (coarse_srate / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF)); - } + symb = (coarse_srate / 100) * 65536; + symb /= (intp->mclk / 100); + } - stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20); - stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (coarse_freq >> 8) & 0xff); - stv0900_write_reg(i_params, R0900_P1_CFRINIT0, coarse_freq & 0xff); - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15); - } - break; - case STV0900_DEMOD_2: - coarse_freq = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8) - | stv0900_read_reg(i_params, R0900_P2_CFR1); - - symb = 13 * (coarse_srate / 10); - - if (symb < i_params->dmd2_symbol_rate) - coarse_srate = 0; - else { - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F); - stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01); - stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20); - stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0x00); - stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2); - stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0); - - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49); - else - stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed); - - if (coarse_srate > 3000000) { - symb = 13 * (coarse_srate / 10); - symb = (symb / 1000) * 65536; - symb /= (i_params->mclk / 1000); - stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF)); - - symb = 10 * (coarse_srate / 13); - symb = (symb / 1000) * 65536; - symb /= (i_params->mclk / 1000); - - stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF)); - - symb = (coarse_srate / 1000) * 65536; - symb /= (i_params->mclk / 1000); - stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF)); - } else { - symb = 13 * (coarse_srate / 10); - symb = (symb / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF)); - - symb = 10 * (coarse_srate / 14); - symb = (symb / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F); - stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF)); - - symb = (coarse_srate / 100) * 65536; - symb /= (i_params->mclk / 100); - stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF); - stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF)); - } + symbcomp = 13 * (coarse_srate / 10); + coarse_freq = (stv0900_read_reg(intp, CFR2) << 8) + | stv0900_read_reg(intp, CFR1); + + if (symbcomp < intp->symbol_rate[demod]) + coarse_srate = 0; + else { + stv0900_write_reg(intp, DMDISTATE, 0x1f); + stv0900_write_reg(intp, TMGCFG2, 0xc1); + stv0900_write_reg(intp, TMGTHRISE, 0x20); + stv0900_write_reg(intp, TMGTHFALL, 0x00); + stv0900_write_reg(intp, TMGCFG, 0xd2); + stv0900_write_bits(intp, CFR_AUTOSCAN, 0); + stv0900_write_reg(intp, AGC2REF, 0x38); + + if (intp->chip_id >= 0x30) + stv0900_write_reg(intp, CARFREQ, 0x79); + else if (intp->chip_id >= 0x20) + stv0900_write_reg(intp, CARFREQ, 0x49); + else + stv0900_write_reg(intp, CARFREQ, 0xed); - stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20); - stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (coarse_freq >> 8) & 0xff); - stv0900_write_reg(i_params, R0900_P2_CFRINIT0, coarse_freq & 0xff); - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15); - } + stv0900_write_reg(intp, SFRUP1, (symbmax >> 8) & 0x7f); + stv0900_write_reg(intp, SFRUP0, (symbmax & 0xff)); - break; + stv0900_write_reg(intp, SFRLOW1, (symbmin >> 8) & 0x7f); + stv0900_write_reg(intp, SFRLOW0, (symbmin & 0xff)); + + stv0900_write_reg(intp, SFRINIT1, (symb >> 8) & 0xff); + stv0900_write_reg(intp, SFRINIT0, (symb & 0xff)); + + stv0900_write_reg(intp, DMDT0M, 0x20); + stv0900_write_reg(intp, CFRINIT1, (coarse_freq >> 8) & 0xff); + stv0900_write_reg(intp, CFRINIT0, coarse_freq & 0xff); + stv0900_write_reg(intp, DMDISTATE, 0x15); } return coarse_srate; @@ -2265,163 +1574,135 @@ static u32 stv0900_search_srate_fine(struct dvb_frontend *fe) static int stv0900_blind_search_algo(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - u8 k_ref_tmg, k_ref_tmg_max, k_ref_tmg_min; - u32 coarse_srate; - int lock = FALSE, coarse_fail = FALSE; - s32 demod_timeout = 500, fec_timeout = 50, kref_tmg_reg, fail_cpt, i, agc2_overflow; - u16 agc2_integr; - u8 dstatus2; - - dprintk(KERN_INFO "%s\n", __func__); - - if (i_params->chip_id < 0x20) { + u8 k_ref_tmg, + k_ref_tmg_max, + k_ref_tmg_min; + u32 coarse_srate, + agc2_th; + int lock = FALSE, + coarse_fail = FALSE; + s32 demod_timeout = 500, + fec_timeout = 50, + fail_cpt, + i, + agc2_overflow; + u16 agc2_int; + u8 dstatus2; + + dprintk("%s\n", __func__); + + if (intp->chip_id < 0x20) { k_ref_tmg_max = 233; k_ref_tmg_min = 143; } else { - k_ref_tmg_max = 120; - k_ref_tmg_min = 30; + k_ref_tmg_max = 110; + k_ref_tmg_min = 10; } - agc2_integr = stv0900_blind_check_agc2_min_level(i_params, demod); - - if (agc2_integr > STV0900_BLIND_SEARCH_AGC2_TH) { - lock = FALSE; - - } else { - switch (demod) { - case STV0900_DEMOD_1: - default: - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xAA); - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55); - - stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xC4); - stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44); - - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41); - stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41); - stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82); - stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0); - } - - kref_tmg_reg = R0900_P1_KREFTMG; - break; - case STV0900_DEMOD_2: - if (i_params->chip_id == 0x10) - stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xAA); - - if (i_params->chip_id < 0x20) - stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55); + if (intp->chip_id <= 0x20) + agc2_th = STV0900_BLIND_SEARCH_AGC2_TH; + else + agc2_th = STV0900_BLIND_SEARCH_AGC2_TH_CUT30; - stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xC4); - stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44); + agc2_int = stv0900_blind_check_agc2_min_level(intp, demod); - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41); - stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41); - stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82); - stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0); - } + if (agc2_int > STV0900_BLIND_SEARCH_AGC2_TH) + return FALSE; - kref_tmg_reg = R0900_P2_KREFTMG; - break; - } + if (intp->chip_id == 0x10) + stv0900_write_reg(intp, CORRELEXP, 0xaa); - k_ref_tmg = k_ref_tmg_max; + if (intp->chip_id < 0x20) + stv0900_write_reg(intp, CARHDR, 0x55); + else + stv0900_write_reg(intp, CARHDR, 0x20); - do { - stv0900_write_reg(i_params, kref_tmg_reg, k_ref_tmg); - if (stv0900_search_srate_coarse(fe) != 0) { - coarse_srate = stv0900_search_srate_fine(fe); + if (intp->chip_id <= 0x20) + stv0900_write_reg(intp, CARCFG, 0xc4); + else + stv0900_write_reg(intp, CARCFG, 0x6); - if (coarse_srate != 0) { - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, coarse_srate, STV0900_BLIND_SEARCH); - lock = stv0900_get_demod_lock(i_params, demod, demod_timeout); - } else - lock = FALSE; - } else { - fail_cpt = 0; - agc2_overflow = 0; + stv0900_write_reg(intp, RTCS2, 0x44); - switch (demod) { - case STV0900_DEMOD_1: - default: - for (i = 0; i < 10; i++) { - agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P1_AGC2I0); + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, EQUALCFG, 0x41); + stv0900_write_reg(intp, FFECFG, 0x41); + stv0900_write_reg(intp, VITSCALE, 0x82); + stv0900_write_reg(intp, VAVSRVIT, 0x0); + } - if (agc2_integr >= 0xff00) - agc2_overflow++; + k_ref_tmg = k_ref_tmg_max; - dstatus2 = stv0900_read_reg(i_params, R0900_P1_DSTATUS2); + do { + stv0900_write_reg(intp, KREFTMG, k_ref_tmg); + if (stv0900_search_srate_coarse(fe) != 0) { + coarse_srate = stv0900_search_srate_fine(fe); + + if (coarse_srate != 0) { + stv0900_get_lock_timeout(&demod_timeout, + &fec_timeout, + coarse_srate, + STV0900_BLIND_SEARCH); + lock = stv0900_get_demod_lock(intp, + demod, + demod_timeout); + } else + lock = FALSE; + } else { + fail_cpt = 0; + agc2_overflow = 0; - if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1)) - fail_cpt++; - } - break; - case STV0900_DEMOD_2: - for (i = 0; i < 10; i++) { - agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8) - | stv0900_read_reg(i_params, R0900_P2_AGC2I0); + for (i = 0; i < 10; i++) { + agc2_int = (stv0900_read_reg(intp, AGC2I1) << 8) + | stv0900_read_reg(intp, AGC2I0); - if (agc2_integr >= 0xff00) - agc2_overflow++; + if (agc2_int >= 0xff00) + agc2_overflow++; - dstatus2 = stv0900_read_reg(i_params, R0900_P2_DSTATUS2); + dstatus2 = stv0900_read_reg(intp, DSTATUS2); - if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1)) - fail_cpt++; - } - break; - } + if (((dstatus2 & 0x1) == 0x1) && + ((dstatus2 >> 7) == 1)) + fail_cpt++; + } - if ((fail_cpt > 7) || (agc2_overflow > 7)) - coarse_fail = TRUE; + if ((fail_cpt > 7) || (agc2_overflow > 7)) + coarse_fail = TRUE; - lock = FALSE; - } - k_ref_tmg -= 30; - } while ((k_ref_tmg >= k_ref_tmg_min) && (lock == FALSE) && (coarse_fail == FALSE)); - } + lock = FALSE; + } + k_ref_tmg -= 30; + } while ((k_ref_tmg >= k_ref_tmg_min) && + (lock == FALSE) && + (coarse_fail == FALSE)); return lock; } -static void stv0900_set_viterbi_acq(struct stv0900_internal *i_params, +static void stv0900_set_viterbi_acq(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - s32 vth_reg; - - dprintk(KERN_INFO "%s\n", __func__); + s32 vth_reg = VTH12; - dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12); + dprintk("%s\n", __func__); - stv0900_write_reg(i_params, vth_reg++, 0x96); - stv0900_write_reg(i_params, vth_reg++, 0x64); - stv0900_write_reg(i_params, vth_reg++, 0x36); - stv0900_write_reg(i_params, vth_reg++, 0x23); - stv0900_write_reg(i_params, vth_reg++, 0x1E); - stv0900_write_reg(i_params, vth_reg++, 0x19); + stv0900_write_reg(intp, vth_reg++, 0x96); + stv0900_write_reg(intp, vth_reg++, 0x64); + stv0900_write_reg(intp, vth_reg++, 0x36); + stv0900_write_reg(intp, vth_reg++, 0x23); + stv0900_write_reg(intp, vth_reg++, 0x1e); + stv0900_write_reg(intp, vth_reg++, 0x19); } -static void stv0900_set_search_standard(struct stv0900_internal *i_params, +static void stv0900_set_search_standard(struct stv0900_internal *intp, enum fe_stv0900_demod_num demod) { - int sstndrd; - - dprintk(KERN_INFO "%s\n", __func__); - - sstndrd = i_params->dmd1_srch_standard; - if (demod == 1) - sstndrd = i_params->dmd2_srch_stndrd; + dprintk("%s\n", __func__); - switch (sstndrd) { + switch (intp->srch_standard[demod]) { case STV0900_SEARCH_DVBS1: dprintk("Search Standard = DVBS1\n"); break; @@ -2436,129 +1717,74 @@ static void stv0900_set_search_standard(struct stv0900_internal *i_params, break; } - switch (demod) { - case STV0900_DEMOD_1: - default: - switch (i_params->dmd1_srch_standard) { - case STV0900_SEARCH_DVBS1: - case STV0900_SEARCH_DSS: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - - stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0); - stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x22); - - stv0900_set_viterbi_acq(i_params, demod); - stv0900_set_viterbi_standard(i_params, - i_params->dmd1_srch_standard, - i_params->dmd1_fec, demod); - - break; - case STV0900_SEARCH_DVBS2: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 1); - stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26); - if (i_params->demod_mode != STV0900_SINGLE) { - if (i_params->chip_id <= 0x11) - stv0900_stop_all_s2_modcod(i_params, demod); - else - stv0900_activate_s2_modcode(i_params, demod); - - } else - stv0900_activate_s2_modcode_single(i_params, demod); - - stv0900_set_viterbi_tracq(i_params, demod); - - break; - case STV0900_AUTO_SEARCH: - default: - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0); - stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26); - if (i_params->demod_mode != STV0900_SINGLE) { - if (i_params->chip_id <= 0x11) - stv0900_stop_all_s2_modcod(i_params, demod); - else - stv0900_activate_s2_modcode(i_params, demod); + switch (intp->srch_standard[demod]) { + case STV0900_SEARCH_DVBS1: + case STV0900_SEARCH_DSS: + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 0); + stv0900_write_bits(intp, STOP_CLKVIT, 0); + stv0900_set_dvbs1_track_car_loop(intp, + demod, + intp->symbol_rate[demod]); + stv0900_write_reg(intp, CAR2CFG, 0x22); + + stv0900_set_viterbi_acq(intp, demod); + stv0900_set_viterbi_standard(intp, + intp->srch_standard[demod], + intp->fec[demod], demod); - } else - stv0900_activate_s2_modcode_single(i_params, demod); + break; + case STV0900_SEARCH_DVBS2: + stv0900_write_bits(intp, DVBS1_ENABLE, 0); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_bits(intp, STOP_CLKVIT, 1); + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ + stv0900_write_reg(intp, CAR2CFG, 0x26); + else + stv0900_write_reg(intp, CAR2CFG, 0x66); - if (i_params->dmd1_symbol_rate >= 2000000) - stv0900_set_viterbi_acq(i_params, demod); + if (intp->demod_mode != STV0900_SINGLE) { + if (intp->chip_id <= 0x11) + stv0900_stop_all_s2_modcod(intp, demod); else - stv0900_set_viterbi_tracq(i_params, demod); + stv0900_activate_s2_modcod(intp, demod); - stv0900_set_viterbi_standard(i_params, i_params->dmd1_srch_standard, i_params->dmd1_fec, demod); + } else + stv0900_activate_s2_modcod_single(intp, demod); - break; - } - break; - case STV0900_DEMOD_2: - switch (i_params->dmd2_srch_stndrd) { - case STV0900_SEARCH_DVBS1: - case STV0900_SEARCH_DSS: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0); - stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x22); - stv0900_set_viterbi_acq(i_params, demod); - stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod); - break; - case STV0900_SEARCH_DVBS2: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 1); - stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26); - if (i_params->demod_mode != STV0900_SINGLE) - stv0900_activate_s2_modcode(i_params, demod); - else - stv0900_activate_s2_modcode_single(i_params, demod); + stv0900_set_viterbi_tracq(intp, demod); - stv0900_set_viterbi_tracq(i_params, demod); - break; - case STV0900_AUTO_SEARCH: - default: - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0); - stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1); - stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1); - stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0); - stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a); - stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09); - stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26); - if (i_params->demod_mode != STV0900_SINGLE) - stv0900_activate_s2_modcode(i_params, demod); - else - stv0900_activate_s2_modcode_single(i_params, demod); + break; + case STV0900_AUTO_SEARCH: + default: + stv0900_write_bits(intp, DVBS1_ENABLE, 1); + stv0900_write_bits(intp, DVBS2_ENABLE, 1); + stv0900_write_bits(intp, STOP_CLKVIT, 0); + stv0900_write_reg(intp, ACLC, 0x1a); + stv0900_write_reg(intp, BCLC, 0x09); + stv0900_set_dvbs1_track_car_loop(intp, + demod, + intp->symbol_rate[demod]); + if (intp->chip_id <= 0x20) /*cut 1.x and 2.0*/ + stv0900_write_reg(intp, CAR2CFG, 0x26); + else + stv0900_write_reg(intp, CAR2CFG, 0x66); - if (i_params->dmd2_symbol_rate >= 2000000) - stv0900_set_viterbi_acq(i_params, demod); + if (intp->demod_mode != STV0900_SINGLE) { + if (intp->chip_id <= 0x11) + stv0900_stop_all_s2_modcod(intp, demod); else - stv0900_set_viterbi_tracq(i_params, demod); + stv0900_activate_s2_modcod(intp, demod); - stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod); + } else + stv0900_activate_s2_modcod_single(intp, demod); - break; - } + stv0900_set_viterbi_tracq(intp, demod); + stv0900_set_viterbi_standard(intp, + intp->srch_standard[demod], + intp->fec[demod], demod); break; } @@ -2567,10 +1793,11 @@ static void stv0900_set_search_standard(struct stv0900_internal *i_params, enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) { struct stv0900_state *state = fe->demodulator_priv; - struct stv0900_internal *i_params = state->internal; + struct stv0900_internal *intp = state->internal; enum fe_stv0900_demod_num demod = state->demod; - s32 demod_timeout = 500, fec_timeout = 50, stream_merger_field; + s32 demod_timeout = 500, fec_timeout = 50; + s32 aq_power, agc1_power, i; int lock = FALSE, low_sr = FALSE; @@ -2578,157 +1805,117 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) enum fe_stv0900_search_algo algo; int no_signal = FALSE; - dprintk(KERN_INFO "%s\n", __func__); + dprintk("%s\n", __func__); - switch (demod) { - case STV0900_DEMOD_1: - default: - algo = i_params->dmd1_srch_algo; - - stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1); - stream_merger_field = F0900_P1_RST_HWARE; - - stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C); - - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x9e); + algo = intp->srch_algo[demod]; + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_reg(intp, DMDISTATE, 0x5c); + if (intp->chip_id >= 0x20) { + if (intp->symbol_rate[demod] > 5000000) + stv0900_write_reg(intp, CORRELABS, 0x9e); else - stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x88); - - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd1_symbol_rate, i_params->dmd1_srch_algo); - - if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) { - i_params->tuner1_bw = 2 * 36000000; - - stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x00); - stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70); - - stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod); - } else { - stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20); - stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2); - - if (i_params->dmd1_symbol_rate < 2000000) - stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x63); - else - stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70); - - stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38); - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0x5a); - - if (i_params->dmd1_srch_algo == STV0900_COLD_START) - i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10; - else if (i_params->dmd1_srch_algo == STV0900_WARM_START) - i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000; - } else { - stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0xc1); - i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10; - } - - stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01); - - stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod); - stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod); - stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod); - if (i_params->dmd1_symbol_rate >= 10000000) - low_sr = FALSE; - else - low_sr = TRUE; - - } - - stv0900_set_tuner(fe, i_params->tuner1_freq, i_params->tuner1_bw); - - stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, i_params->dmd1_srch_iq_inv); - stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1); + stv0900_write_reg(intp, CORRELABS, 0x82); + } else + stv0900_write_reg(intp, CORRELABS, 0x88); - stv0900_set_search_standard(i_params, demod); + stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, + intp->symbol_rate[demod], + intp->srch_algo[demod]); - if (i_params->dmd1_srch_algo != STV0900_BLIND_SEARCH) - stv0900_start_search(i_params, demod); - break; - case STV0900_DEMOD_2: - algo = i_params->dmd2_srch_algo; + if (intp->srch_algo[demod] == STV0900_BLIND_SEARCH) { + intp->bw[demod] = 2 * 36000000; - stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1); + stv0900_write_reg(intp, TMGCFG2, 0xc0); + stv0900_write_reg(intp, CORRELMANT, 0x70); - stream_merger_field = F0900_P2_RST_HWARE; - - stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C); + stv0900_set_symbol_rate(intp, intp->mclk, 1000000, demod); + } else { + stv0900_write_reg(intp, DMDT0M, 0x20); + stv0900_write_reg(intp, TMGCFG, 0xd2); - if (i_params->chip_id >= 0x20) - stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x9e); + if (intp->symbol_rate[demod] < 2000000) + stv0900_write_reg(intp, CORRELMANT, 0x63); else - stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x88); + stv0900_write_reg(intp, CORRELMANT, 0x70); - stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd2_symbol_rate, i_params->dmd2_srch_algo); + stv0900_write_reg(intp, AGC2REF, 0x38); - if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) { - i_params->tuner2_bw = 2 * 36000000; + intp->bw[demod] = + stv0900_carrier_width(intp->symbol_rate[demod], + intp->rolloff); + if (intp->chip_id >= 0x20) { + stv0900_write_reg(intp, KREFTMG, 0x5a); - stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x00); - stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70); + if (intp->srch_algo[demod] == STV0900_COLD_START) { + intp->bw[demod] += 10000000; + intp->bw[demod] *= 15; + intp->bw[demod] /= 10; + } else if (intp->srch_algo[demod] == STV0900_WARM_START) + intp->bw[demod] += 10000000; - stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod); } else { - stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20); - stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2); + stv0900_write_reg(intp, KREFTMG, 0xc1); + intp->bw[demod] += 10000000; + intp->bw[demod] *= 15; + intp->bw[demod] /= 10; + } - if (i_params->dmd2_symbol_rate < 2000000) - stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x63); - else - stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70); + stv0900_write_reg(intp, TMGCFG2, 0xc1); - if (i_params->dmd2_symbol_rate >= 10000000) - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38); - else - stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x60); + stv0900_set_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + stv0900_set_max_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + stv0900_set_min_symbol_rate(intp, intp->mclk, + intp->symbol_rate[demod], demod); + if (intp->symbol_rate[demod] >= 10000000) + low_sr = FALSE; + else + low_sr = TRUE; - if (i_params->chip_id >= 0x20) { - stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0x5a); + } - if (i_params->dmd2_srch_algo == STV0900_COLD_START) - i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate, - i_params->rolloff) + 10000000)) / 10; - else if (i_params->dmd2_srch_algo == STV0900_WARM_START) - i_params->tuner2_bw = stv0900_carrier_width(i_params->dmd2_symbol_rate, - i_params->rolloff) + 10000000; - } else { - stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0xc1); - i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate, - i_params->rolloff) + 10000000)) / 10; - } + stv0900_set_tuner(fe, intp->freq[demod], intp->bw[demod]); - stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01); + agc1_power = MAKEWORD(stv0900_get_bits(intp, AGCIQ_VALUE1), + stv0900_get_bits(intp, AGCIQ_VALUE0)); - stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod); - stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod); - stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod); - if (i_params->dmd2_symbol_rate >= 10000000) - low_sr = FALSE; - else - low_sr = TRUE; + aq_power = 0; - } + if (agc1_power == 0) { + for (i = 0; i < 5; i++) + aq_power += (stv0900_get_bits(intp, POWER_I) + + stv0900_get_bits(intp, POWER_Q)) / 2; - stv0900_set_tuner(fe, i_params->tuner2_freq, i_params->tuner2_bw); + aq_power /= 5; + } - stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, i_params->dmd2_srch_iq_inv); - stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1); + if ((agc1_power == 0) && (aq_power < IQPOWER_THRESHOLD)) { + intp->result[demod].locked = FALSE; + signal_type = STV0900_NOAGC1; + dprintk("%s: NO AGC1, POWERI, POWERQ\n", __func__); + } else { + stv0900_write_bits(intp, SPECINV_CONTROL, + intp->srch_iq_inv[demod]); + if (intp->chip_id <= 0x20) /*cut 2.0*/ + stv0900_write_bits(intp, MANUALSX_ROLLOFF, 1); + else /*cut 3.0*/ + stv0900_write_bits(intp, MANUALS2_ROLLOFF, 1); - stv0900_set_search_standard(i_params, demod); + stv0900_set_search_standard(intp, demod); - if (i_params->dmd2_srch_algo != STV0900_BLIND_SEARCH) - stv0900_start_search(i_params, demod); - break; + if (intp->srch_algo[demod] != STV0900_BLIND_SEARCH) + stv0900_start_search(intp, demod); } - if (i_params->chip_id == 0x12) { - stv0900_write_bits(i_params, stream_merger_field, 0); + if (signal_type == STV0900_NOAGC1) + return signal_type; + + if (intp->chip_id == 0x12) { + stv0900_write_bits(intp, RST_HWARE, 0); msleep(3); - stv0900_write_bits(i_params, stream_merger_field, 1); - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); } if (algo == STV0900_BLIND_SEARCH) @@ -2736,12 +1923,12 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) else if (algo == STV0900_COLD_START) lock = stv0900_get_demod_cold_lock(fe, demod_timeout); else if (algo == STV0900_WARM_START) - lock = stv0900_get_demod_lock(i_params, demod, demod_timeout); + lock = stv0900_get_demod_lock(intp, demod, demod_timeout); if ((lock == FALSE) && (algo == STV0900_COLD_START)) { if (low_sr == FALSE) { - if (stv0900_check_timing_lock(i_params, demod) == TRUE) - lock = stv0900_sw_algo(i_params, demod); + if (stv0900_check_timing_lock(intp, demod) == TRUE) + lock = stv0900_sw_algo(intp, demod); } } @@ -2750,98 +1937,64 @@ enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe) if ((lock == TRUE) && (signal_type == STV0900_RANGEOK)) { stv0900_track_optimization(fe); - if (i_params->chip_id <= 0x11) { - if ((stv0900_get_standard(fe, STV0900_DEMOD_1) == STV0900_DVBS1_STANDARD) && (stv0900_get_standard(fe, STV0900_DEMOD_2) == STV0900_DVBS1_STANDARD)) { + if (intp->chip_id <= 0x11) { + if ((stv0900_get_standard(fe, 0) == + STV0900_DVBS1_STANDARD) && + (stv0900_get_standard(fe, 1) == + STV0900_DVBS1_STANDARD)) { msleep(20); - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 0); } else { - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 0); msleep(3); - stv0900_write_bits(i_params, stream_merger_field, 1); - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); } - } else if (i_params->chip_id == 0x20) { - stv0900_write_bits(i_params, stream_merger_field, 0); + + } else if (intp->chip_id >= 0x20) { + stv0900_write_bits(intp, RST_HWARE, 0); msleep(3); - stv0900_write_bits(i_params, stream_merger_field, 1); - stv0900_write_bits(i_params, stream_merger_field, 0); + stv0900_write_bits(intp, RST_HWARE, 1); + stv0900_write_bits(intp, RST_HWARE, 0); } - if (stv0900_wait_for_lock(i_params, demod, fec_timeout, fec_timeout) == TRUE) { + if (stv0900_wait_for_lock(intp, demod, + fec_timeout, fec_timeout) == TRUE) { lock = TRUE; - switch (demod) { - case STV0900_DEMOD_1: - default: - i_params->dmd1_rslts.locked = TRUE; - if (i_params->dmd1_rslts.standard == STV0900_DVBS2_STANDARD) { - stv0900_set_dvbs2_rolloff(i_params, demod); - stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0x40); - stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67); - } else { - stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75); - } - - stv0900_write_reg(i_params, R0900_P1_FBERCPT4, 0); - stv0900_write_reg(i_params, R0900_P1_ERRCTRL2, 0xc1); - break; - case STV0900_DEMOD_2: - i_params->dmd2_rslts.locked = TRUE; - - if (i_params->dmd2_rslts.standard == STV0900_DVBS2_STANDARD) { - stv0900_set_dvbs2_rolloff(i_params, demod); - stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x60); - stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x20); - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67); - } else { - stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75); - } - - stv0900_write_reg(i_params, R0900_P2_FBERCPT4, 0); - - stv0900_write_reg(i_params, R0900_P2_ERRCTRL2, 0xc1); - break; + intp->result[demod].locked = TRUE; + if (intp->result[demod].standard == + STV0900_DVBS2_STANDARD) { + stv0900_set_dvbs2_rolloff(intp, demod); + stv0900_write_bits(intp, RESET_UPKO_COUNT, 1); + stv0900_write_bits(intp, RESET_UPKO_COUNT, 0); + stv0900_write_reg(intp, ERRCTRL1, 0x67); + } else { + stv0900_write_reg(intp, ERRCTRL1, 0x75); } + + stv0900_write_reg(intp, FBERCPT4, 0); + stv0900_write_reg(intp, ERRCTRL2, 0xc1); } else { lock = FALSE; signal_type = STV0900_NODATA; - no_signal = stv0900_check_signal_presence(i_params, demod); - - switch (demod) { - case STV0900_DEMOD_1: - default: - i_params->dmd1_rslts.locked = FALSE; - break; - case STV0900_DEMOD_2: - i_params->dmd2_rslts.locked = FALSE; - break; - } + no_signal = stv0900_check_signal_presence(intp, demod); + + intp->result[demod].locked = FALSE; } } - if ((signal_type == STV0900_NODATA) && (no_signal == FALSE)) { - switch (demod) { - case STV0900_DEMOD_1: - default: - if (i_params->chip_id <= 0x11) { - if ((stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) && - (i_params->dmd1_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST)) - signal_type = stv0900_dvbs1_acq_workaround(fe); - } else - i_params->dmd1_rslts.locked = FALSE; + if ((signal_type != STV0900_NODATA) || (no_signal != FALSE)) + return signal_type; - break; - case STV0900_DEMOD_2: - if (i_params->chip_id <= 0x11) { - if ((stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) && - (i_params->dmd2_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST)) - signal_type = stv0900_dvbs1_acq_workaround(fe); - } else - i_params->dmd2_rslts.locked = FALSE; - break; - } + if (intp->chip_id > 0x11) { + intp->result[demod].locked = FALSE; + return signal_type; } + if ((stv0900_get_bits(intp, HEADER_MODE) == STV0900_DVBS_FOUND) && + (intp->srch_iq_inv[demod] <= STV0900_IQ_AUTO_NORMAL_FIRST)) + signal_type = stv0900_dvbs1_acq_workaround(fe); + return signal_type; } diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 488bdfb34fb32..48edd542242ea 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -825,7 +825,7 @@ static int stv090x_set_min_srate(struct stv090x_state *state, u32 clk, u32 srate sym /= (state->mclk >> 7); } - if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0xff)) < 0) /* MSB */ + if (STV090x_WRITE_DEMOD(state, SFRLOW1, ((sym >> 8) & 0x7f)) < 0) /* MSB */ goto err; if (STV090x_WRITE_DEMOD(state, SFRLOW0, (sym & 0xff)) < 0) /* LSB */ goto err; @@ -1238,6 +1238,8 @@ static int stv090x_delivery_search(struct stv090x_state *state) goto err; } + if (stv090x_set_vit_thtracq(state) < 0) + goto err; break; case STV090x_SEARCH_AUTO: @@ -1278,17 +1280,8 @@ static int stv090x_delivery_search(struct stv090x_state *state) goto err; } - if (state->srate >= 2000000) { - /* Srate >= 2MSPS, Viterbi threshold to acquire */ - if (stv090x_set_vit_thacq(state) < 0) - goto err; - } else { - /* Srate < 2MSPS, Reset Viterbi thresholdto track - * and then re-acquire - */ - if (stv090x_set_vit_thtracq(state) < 0) - goto err; - } + if (stv090x_set_vit_thacq(state) < 0) + goto err; if (stv090x_set_viterbi(state) < 0) goto err; @@ -1317,7 +1310,7 @@ static int stv090x_start_search(struct stv090x_state *state) goto err; if (STV090x_WRITE_DEMOD(state, CFRUP1, 0x0f) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, CFRUP1, 0xff) < 0) + if (STV090x_WRITE_DEMOD(state, CFRUP0, 0xff) < 0) goto err; if (STV090x_WRITE_DEMOD(state, CFRLOW1, 0xf0) < 0) goto err; @@ -1371,7 +1364,7 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, CFRUP1, MSB(freq)) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, CFRUP1, LSB(freq)) < 0) + if (STV090x_WRITE_DEMOD(state, CFRUP0, LSB(freq)) < 0) goto err; freq *= -1; @@ -1422,6 +1415,9 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DMDCFG2, reg) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, RTC, 0x88) < 0) + goto err; + if (state->dev_ver >= 0x20) { /*Frequency offset detector setting*/ if (state->srate < 2000000) { @@ -1430,20 +1426,22 @@ static int stv090x_start_search(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x39) < 0) goto err; } else { - /* Cut 2 */ + /* Cut 3 */ if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x89) < 0) goto err; } if (STV090x_WRITE_DEMOD(state, CARHDR, 0x40) < 0) goto err; - } - - if (state->srate < 10000000) { + } else if (state->srate < 10000000) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4c) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) + goto err; } else { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x4b) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, CARHDR, 0x20) < 0) + goto err; } } else { if (state->srate < 10000000) { @@ -1485,14 +1483,14 @@ static int stv090x_start_search(struct stv090x_state *state) static int stv090x_get_agc2_min_level(struct stv090x_state *state) { - u32 agc2_min = 0, agc2 = 0, freq_init, freq_step, reg; + u32 agc2_min = 0xffff, agc2 = 0, freq_init, freq_step, reg; s32 i, j, steps, dir; if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) goto err; reg = STV090x_READ_DEMOD(state, DMDCFGMD); - STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1); - STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 1); + STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 0); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) goto err; @@ -1509,10 +1507,8 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state) if (stv090x_set_srate(state, 1000000) < 0) goto err; - steps = -1 + state->search_range / 1000000; - steps /= 2; - steps = (2 * steps) + 1; - if (steps < 0) + steps = state->search_range / 1000000; + if (steps <= 0) steps = 1; dir = 1; @@ -1525,7 +1521,7 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state) else freq_init = freq_init - (freq_step * i); - dir = -1; + dir *= -1; if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5c) < 0) /* Demod RESET */ goto err; @@ -1536,13 +1532,14 @@ static int stv090x_get_agc2_min_level(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x58) < 0) /* Demod RESET */ goto err; msleep(10); + + agc2 = 0; for (j = 0; j < 10; j++) { - agc2 += STV090x_READ_DEMOD(state, AGC2I1) << 8; - agc2 |= STV090x_READ_DEMOD(state, AGC2I0); + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); } agc2 /= 10; - agc2_min = 0xffff; - if (agc2 < 0xffff) + if (agc2 < agc2_min) agc2_min = agc2; } @@ -1584,6 +1581,12 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) int tmg_lock = 0, i; s32 tmg_cpt = 0, dir = 1, steps, cur_step = 0, freq; u32 srate_coarse = 0, agc2 = 0, car_step = 1200, reg; + u32 agc2th; + + if (state->dev_ver >= 0x30) + agc2th = 0x2e00; + else + agc2th = 0x1f00; reg = STV090x_READ_DEMOD(state, DMDISTATE); STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x1f); /* Demod RESET */ @@ -1591,13 +1594,15 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) goto err; if (STV090x_WRITE_DEMOD(state, TMGCFG, 0x12) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc0) < 0) + goto err; if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0xf0) < 0) goto err; if (STV090x_WRITE_DEMOD(state, TMGTHFALL, 0xe0) < 0) goto err; reg = STV090x_READ_DEMOD(state, DMDCFGMD); STV090x_SETFIELD_Px(reg, SCAN_ENABLE_FIELD, 1); - STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 1); + STV090x_SETFIELD_Px(reg, CFR_AUTOSCAN_FIELD, 0); if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) goto err; @@ -1611,13 +1616,13 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) goto err; if (STV090x_WRITE_DEMOD(state, DMDTOM, 0x00) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x60) < 0) + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x50) < 0) goto err; if (state->dev_ver >= 0x30) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x99) < 0) goto err; - if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x95) < 0) + if (STV090x_WRITE_DEMOD(state, SFRSTEP, 0x98) < 0) goto err; } else if (state->dev_ver >= 0x20) { @@ -1652,23 +1657,31 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) while ((!tmg_lock) && (cur_step < steps)) { if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x5f) < 0) /* Demod RESET */ goto err; - reg = STV090x_READ_DEMOD(state, DMDISTATE); - STV090x_SETFIELD_Px(reg, I2C_DEMOD_MODE_FIELD, 0x00); /* trigger acquisition */ - if (STV090x_WRITE_DEMOD(state, DMDISTATE, reg) < 0) + if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT1, 0x00) < 0) + goto err; + if (STV090x_WRITE_DEMOD(state, SFRINIT0, 0x00) < 0) + goto err; + /* trigger acquisition */ + if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x40) < 0) goto err; msleep(50); for (i = 0; i < 10; i++) { reg = STV090x_READ_DEMOD(state, DSTATUS); if (STV090x_GETFIELD_Px(reg, TMGLOCK_QUALITY_FIELD) >= 2) tmg_cpt++; - agc2 += STV090x_READ_DEMOD(state, AGC2I1) << 8; - agc2 |= STV090x_READ_DEMOD(state, AGC2I0); + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); } agc2 /= 10; srate_coarse = stv090x_get_srate(state, state->mclk); cur_step++; dir *= -1; - if ((tmg_cpt >= 5) && (agc2 < 0x1f00) && (srate_coarse < 55000000) && (srate_coarse > 850000)) + if ((tmg_cpt >= 5) && (agc2 < agc2th) && + (srate_coarse < 50000000) && (srate_coarse > 850000)) tmg_lock = 1; else if (cur_step < steps) { if (dir > 0) @@ -1681,7 +1694,7 @@ static u32 stv090x_srate_srch_coarse(struct stv090x_state *state) goto err; if (state->config->tuner_set_frequency) { - if (state->config->tuner_set_frequency(fe, state->frequency) < 0) + if (state->config->tuner_set_frequency(fe, freq) < 0) goto err; } @@ -1738,7 +1751,7 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state) else { if (STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1f) < 0) /* Demod RESET */ goto err; - if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0x01) < 0) + if (STV090x_WRITE_DEMOD(state, TMGCFG2, 0xc1) < 0) goto err; if (STV090x_WRITE_DEMOD(state, TMGTHRISE, 0x20) < 0) goto err; @@ -1751,6 +1764,9 @@ static u32 stv090x_srate_srch_fine(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) goto err; + if (STV090x_WRITE_DEMOD(state, AGC2REF, 0x38) < 0) + goto err; + if (state->dev_ver >= 0x30) { if (STV090x_WRITE_DEMOD(state, CARFREQ, 0x79) < 0) goto err; @@ -1856,12 +1872,12 @@ static int stv090x_get_dmdlock(struct stv090x_state *state, s32 timeout) static int stv090x_blind_search(struct stv090x_state *state) { u32 agc2, reg, srate_coarse; - s32 timeout_dmd = 500, cpt_fail, agc2_ovflw, i; + s32 cpt_fail, agc2_ovflw, i; u8 k_ref, k_max, k_min; int coarse_fail, lock; - k_max = 120; - k_min = 30; + k_max = 110; + k_min = 10; agc2 = stv090x_get_agc2_min_level(state); @@ -1900,7 +1916,8 @@ static int stv090x_blind_search(struct stv090x_state *state) srate_coarse = stv090x_srate_srch_fine(state); if (srate_coarse != 0) { stv090x_get_lock_tmg(state); - lock = stv090x_get_dmdlock(state, timeout_dmd); + lock = stv090x_get_dmdlock(state, + state->DemodTimeout); } else { lock = 0; } @@ -1908,8 +1925,8 @@ static int stv090x_blind_search(struct stv090x_state *state) cpt_fail = 0; agc2_ovflw = 0; for (i = 0; i < 10; i++) { - agc2 = STV090x_READ_DEMOD(state, AGC2I1) << 8; - agc2 |= STV090x_READ_DEMOD(state, AGC2I0); + agc2 += (STV090x_READ_DEMOD(state, AGC2I1) << 8) | + STV090x_READ_DEMOD(state, AGC2I0); if (agc2 >= 0xff00) agc2_ovflw++; reg = STV090x_READ_DEMOD(state, DSTATUS2); @@ -1923,7 +1940,7 @@ static int stv090x_blind_search(struct stv090x_state *state) lock = 0; } - k_ref -= 30; + k_ref -= 20; } while ((k_ref >= k_min) && (!lock) && (!coarse_fail)); } @@ -2062,7 +2079,7 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) goto err; if (state->config->tuner_set_frequency) { - if (state->config->tuner_set_frequency(fe, state->frequency) < 0) + if (state->config->tuner_set_frequency(fe, freq) < 0) goto err; } @@ -2093,17 +2110,6 @@ static int stv090x_get_coldlock(struct stv090x_state *state, s32 timeout_dmd) goto err; STV090x_WRITE_DEMOD(state, DMDISTATE, 0x1c); - if (state->delsys == STV090x_DVBS2) { - reg = STV090x_READ_DEMOD(state, DMDCFGMD); - STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 0); - STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); - if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) - goto err; - STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); - STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 1); - if (STV090x_WRITE_DEMOD(state, DMDCFGMD, reg) < 0) - goto err; - } if (STV090x_WRITE_DEMOD(state, CFRINIT1, 0x00) < 0) goto err; if (STV090x_WRITE_DEMOD(state, CFRINIT0, 0x00) < 0) @@ -2425,7 +2431,7 @@ static s32 stv090x_get_car_freq(struct stv090x_state *state, u32 mclk) derot = (int_1 * int_2) + ((int_1 * tmp_2) >> 12) + - ((int_1 * tmp_1) >> 12); + ((int_2 * tmp_1) >> 12); return derot; } @@ -2732,7 +2738,7 @@ static int stv090x_optimize_track(struct stv090x_state *state) switch (state->delsys) { case STV090x_DVBS1: case STV090x_DSS: - if (state->algo == STV090x_SEARCH_AUTO) { + if (state->search_mode == STV090x_SEARCH_AUTO) { reg = STV090x_READ_DEMOD(state, DMDCFGMD); STV090x_SETFIELD_Px(reg, DVBS1_ENABLE_FIELD, 1); STV090x_SETFIELD_Px(reg, DVBS2_ENABLE_FIELD, 0); @@ -2857,6 +2863,9 @@ static int stv090x_optimize_track(struct stv090x_state *state) if (stv090x_set_srate(state, srate) < 0) goto err; blind_tune = 1; + + if (stv090x_dvbs_track_crl(state) < 0) + goto err; } if (state->dev_ver >= 0x20) { @@ -3042,7 +3051,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) struct dvb_frontend *fe = &state->frontend; enum stv090x_signal_state signal_state = STV090x_NOCARRIER; u32 reg; - s32 timeout_dmd = 500, timeout_fec = 50, agc1_power, power_iq = 0, i; + s32 agc1_power, power_iq = 0, i; int lock = 0, low_sr = 0, no_signal = 0; reg = STV090x_READ_DEMOD(state, TSCFGH); @@ -3054,8 +3063,13 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) goto err; if (state->dev_ver >= 0x20) { - if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) /* cut 2.0 */ - goto err; + if (state->srate > 5000000) { + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x9e) < 0) + goto err; + } else { + if (STV090x_WRITE_DEMOD(state, CORRELABS, 0x82) < 0) + goto err; + } } stv090x_get_lock_tmg(state); @@ -3175,7 +3189,7 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if ((agc1_power == 0) && (power_iq < STV090x_IQPOWER_THRESHOLD)) { dprintk(FE_ERROR, 1, "No Signal: POWER_IQ=0x%02x", power_iq); lock = 0; - + signal_state = STV090x_NOAGC1; } else { reg = STV090x_READ_DEMOD(state, DEMOD); STV090x_SETFIELD_Px(reg, SPECINV_CONTROL_FIELD, state->inversion); @@ -3199,18 +3213,17 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) } } - /* need to check for AGC1 state */ - - + if (signal_state == STV090x_NOAGC1) + return signal_state; if (state->algo == STV090x_BLIND_SEARCH) lock = stv090x_blind_search(state); else if (state->algo == STV090x_COLD_SEARCH) - lock = stv090x_get_coldlock(state, timeout_dmd); + lock = stv090x_get_coldlock(state, state->DemodTimeout); else if (state->algo == STV090x_WARM_SEARCH) - lock = stv090x_get_dmdlock(state, timeout_dmd); + lock = stv090x_get_dmdlock(state, state->DemodTimeout); if ((!lock) && (state->algo == STV090x_COLD_SEARCH)) { if (!low_sr) { @@ -3245,8 +3258,9 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) goto err; } - if (stv090x_get_lock(state, timeout_fec, timeout_fec)) { - lock = 1; + lock = stv090x_get_lock(state, state->FecTimeout, + state->FecTimeout); + if (lock) { if (state->delsys == STV090x_DVBS2) { stv090x_set_s2rolloff(state); @@ -3273,7 +3287,6 @@ static enum stv090x_signal_state stv090x_algo(struct stv090x_state *state) if (STV090x_WRITE_DEMOD(state, ERRCTRL2, 0xc1) < 0) goto err; } else { - lock = 0; signal_state = STV090x_NODATA; no_signal = stv090x_chk_signal(state); } @@ -3296,7 +3309,13 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron state->search_mode = STV090x_SEARCH_AUTO; state->algo = STV090x_COLD_SEARCH; state->fec = STV090x_PRERR; - state->search_range = 2000000; + if (state->srate > 10000000) { + dprintk(FE_DEBUG, 1, "Search range: 10 MHz"); + state->search_range = 10000000; + } else { + dprintk(FE_DEBUG, 1, "Search range: 5 MHz"); + state->search_range = 5000000; + } if (stv090x_algo(state) == STV090x_RANGEOK) { dprintk(FE_DEBUG, 1, "Search success!"); @@ -3309,7 +3328,6 @@ static enum dvbfe_search stv090x_search(struct dvb_frontend *fe, struct dvb_fron return DVBFE_ALGO_SEARCH_ERROR; } -/* FIXME! */ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) { struct stv090x_state *state = fe->demodulator_priv; @@ -3331,9 +3349,15 @@ static int stv090x_read_status(struct dvb_frontend *fe, enum fe_status *status) dprintk(FE_DEBUG, 1, "Delivery system: DVB-S2"); reg = STV090x_READ_DEMOD(state, DSTATUS); if (STV090x_GETFIELD_Px(reg, LOCK_DEFINITIF_FIELD)) { - reg = STV090x_READ_DEMOD(state, TSSTATUS); - if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) { - *status = FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK; + reg = STV090x_READ_DEMOD(state, PDELSTATUS1); + if (STV090x_GETFIELD_Px(reg, PKTDELIN_LOCK_FIELD)) { + reg = STV090x_READ_DEMOD(state, TSSTATUS); + if (STV090x_GETFIELD_Px(reg, TSFIFO_LINEOK_FIELD)) { + *status = FE_HAS_CARRIER | + FE_HAS_VITERBI | + FE_HAS_SYNC | + FE_HAS_LOCK; + } } } break; @@ -3412,14 +3436,12 @@ static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val) int res = 0; int min = 0, med; - if (val < tab[min].read) - res = tab[min].real; - else if (val >= tab[max].read) - res = tab[max].real; - else { + if ((val >= tab[min].read && val < tab[max].read) || + (val >= tab[max].read && val < tab[min].read)) { while ((max - min) > 1) { med = (max + min) / 2; - if (val >= tab[min].read && val < tab[med].read) + if ((val >= tab[min].read && val < tab[med].read) || + (val >= tab[med].read && val < tab[min].read)) max = med; else min = med; @@ -3428,6 +3450,18 @@ static int stv090x_table_lookup(const struct stv090x_tab *tab, int max, int val) (tab[max].real - tab[min].real) / (tab[max].read - tab[min].read)) + tab[min].real; + } else { + if (tab[min].read < tab[max].read) { + if (val < tab[min].read) + res = tab[min].real; + else if (val >= tab[max].read) + res = tab[max].real; + } else { + if (val >= tab[min].read) + res = tab[min].real; + else if (val < tab[max].read) + res = tab[max].real; + } } return res; @@ -3437,16 +3471,22 @@ static int stv090x_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { struct stv090x_state *state = fe->demodulator_priv; u32 reg; - s32 agc; + s32 agc_0, agc_1, agc; + s32 str; reg = STV090x_READ_DEMOD(state, AGCIQIN1); - agc = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); + agc_1 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); + reg = STV090x_READ_DEMOD(state, AGCIQIN0); + agc_0 = STV090x_GETFIELD_Px(reg, AGCIQ_VALUE_FIELD); + agc = MAKEWORD16(agc_1, agc_0); - *strength = stv090x_table_lookup(stv090x_rf_tab, ARRAY_SIZE(stv090x_rf_tab) - 1, agc); + str = stv090x_table_lookup(stv090x_rf_tab, + ARRAY_SIZE(stv090x_rf_tab) - 1, agc); if (agc > stv090x_rf_tab[0].read) - *strength = 5; + str = 0; else if (agc < stv090x_rf_tab[ARRAY_SIZE(stv090x_rf_tab) - 1].read) - *strength = -100; + str = -100; + *strength = (str + 100) * 0xFFFF / 100; return 0; } @@ -3457,6 +3497,8 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) u32 reg_0, reg_1, reg, i; s32 val_0, val_1, val = 0; u8 lock_f; + s32 div; + u32 last; switch (state->delsys) { case STV090x_DVBS2: @@ -3468,14 +3510,15 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) reg_1 = STV090x_READ_DEMOD(state, NNOSPLHT1); val_1 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD); reg_0 = STV090x_READ_DEMOD(state, NNOSPLHT0); - val_0 = STV090x_GETFIELD_Px(reg_1, NOSPLHT_NORMED_FIELD); + val_0 = STV090x_GETFIELD_Px(reg_0, NOSPLHT_NORMED_FIELD); val += MAKEWORD16(val_1, val_0); msleep(1); } val /= 16; - *cnr = stv090x_table_lookup(stv090x_s2cn_tab, ARRAY_SIZE(stv090x_s2cn_tab) - 1, val); - if (val < stv090x_s2cn_tab[ARRAY_SIZE(stv090x_s2cn_tab) - 1].read) - *cnr = 1000; + last = ARRAY_SIZE(stv090x_s2cn_tab) - 1; + div = stv090x_s2cn_tab[0].read - + stv090x_s2cn_tab[last].read; + *cnr = 0xFFFF - ((val * 0xFFFF) / div); } break; @@ -3489,14 +3532,15 @@ static int stv090x_read_cnr(struct dvb_frontend *fe, u16 *cnr) reg_1 = STV090x_READ_DEMOD(state, NOSDATAT1); val_1 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD); reg_0 = STV090x_READ_DEMOD(state, NOSDATAT0); - val_0 = STV090x_GETFIELD_Px(reg_1, NOSDATAT_UNNORMED_FIELD); + val_0 = STV090x_GETFIELD_Px(reg_0, NOSDATAT_UNNORMED_FIELD); val += MAKEWORD16(val_1, val_0); msleep(1); } val /= 16; - *cnr = stv090x_table_lookup(stv090x_s1cn_tab, ARRAY_SIZE(stv090x_s1cn_tab) - 1, val); - if (val < stv090x_s2cn_tab[ARRAY_SIZE(stv090x_s1cn_tab) - 1].read) - *cnr = 1000; + last = ARRAY_SIZE(stv090x_s1cn_tab) - 1; + div = stv090x_s1cn_tab[0].read - + stv090x_s1cn_tab[last].read; + *cnr = 0xFFFF - ((val * 0xFFFF) / div); } break; default: @@ -3732,6 +3776,8 @@ static int stv090x_ldpc_mode(struct stv090x_state *state, enum stv090x_mode ldpc { u32 reg = 0; + reg = stv090x_read_reg(state, STV090x_GENCFG); + switch (ldpc_mode) { case STV090x_DUAL: default: diff --git a/drivers/media/dvb/frontends/stv090x_priv.h b/drivers/media/dvb/frontends/stv090x_priv.h index 5a4a01740d881..5921a8d6c89f0 100644 --- a/drivers/media/dvb/frontends/stv090x_priv.h +++ b/drivers/media/dvb/frontends/stv090x_priv.h @@ -83,7 +83,7 @@ #define STV090x_IQPOWER_THRESHOLD 30 #define STV090x_SEARCH_AGC2_TH_CUT20 700 -#define STV090x_SEARCH_AGC2_TH_CUT30 1200 +#define STV090x_SEARCH_AGC2_TH_CUT30 1400 #define STV090x_SEARCH_AGC2_TH(__ver) \ ((__ver <= 0x20) ? \ @@ -91,6 +91,7 @@ STV090x_SEARCH_AGC2_TH_CUT30) enum stv090x_signal_state { + STV090x_NOAGC1, STV090x_NOCARRIER, STV090x_NODATA, STV090x_DATAOK, diff --git a/drivers/media/dvb/frontends/stv090x_reg.h b/drivers/media/dvb/frontends/stv090x_reg.h index 57b6abbbd32d2..2502855dd7847 100644 --- a/drivers/media/dvb/frontends/stv090x_reg.h +++ b/drivers/media/dvb/frontends/stv090x_reg.h @@ -44,7 +44,7 @@ #define STV090x_OFFST_OUTSERRS2_HZ_FIELD 5 #define STV090x_WIDTH_OUTSERRS2_HZ_FIELD 1 #define STV090x_OFFST_OUTSERRS3_HZ_FIELD 4 -#define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1 +#define STV090x_WIDTH_OUTSERRS3_HZ_FIELD 1 #define STV090x_OFFST_OUTPARRS3_HZ_FIELD 3 #define STV090x_WIDTH_OUTPARRS3_HZ_FIELD 1 @@ -113,24 +113,24 @@ #define STV090x_IRQMASK3 0xf124 #define STV090x_OFFST_MPLL_LOCK_FIELD 5 #define STV090x_WIDTH_MPLL_LOCK_FIELD 1 -#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 2 -#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 3 -#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 2 -#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 3 +#define STV090x_OFFST_MSTREAM_LCK_3_FIELD 4 +#define STV090x_WIDTH_MSTREAM_LCK_3_FIELD 1 +#define STV090x_OFFST_MSTREAM_LCK_2_FIELD 3 +#define STV090x_WIDTH_MSTREAM_LCK_2_FIELD 1 #define STV090x_OFFST_MSTREAM_LCK_1_FIELD 2 -#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 3 +#define STV090x_WIDTH_MSTREAM_LCK_1_FIELD 1 #define STV090x_OFFST_MDVBS1_PRF_2_FIELD 1 #define STV090x_WIDTH_MDVBS1_PRF_2_FIELD 1 #define STV090x_OFFST_MDVBS1_PRF_1_FIELD 0 #define STV090x_WIDTH_MDVBS1_PRF_1_FIELD 1 #define STV090x_IRQMASK2 0xf125 -#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 5 -#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 3 -#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 5 -#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 3 +#define STV090x_OFFST_MSPY_ENDSIM_3_FIELD 7 +#define STV090x_WIDTH_MSPY_ENDSIM_3_FIELD 1 +#define STV090x_OFFST_MSPY_ENDSIM_2_FIELD 6 +#define STV090x_WIDTH_MSPY_ENDSIM_2_FIELD 1 #define STV090x_OFFST_MSPY_ENDSIM_1_FIELD 5 -#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 3 +#define STV090x_WIDTH_MSPY_ENDSIM_1_FIELD 1 #define STV090x_OFFST_MPKTDEL_ERROR_2_FIELD 4 #define STV090x_WIDTH_MPKTDEL_ERROR_2_FIELD 1 #define STV090x_OFFST_MPKTDEL_LOCKB_2_FIELD 3 @@ -370,7 +370,7 @@ #define STV090x_OFFST_SELX1RATIO_FIELD 5 #define STV090x_WIDTH_SELX1RATIO_FIELD 1 #define STV090x_OFFST_STOP_PLL_FIELD 3 -#define STV090x_WIDTH_SELX1RATIO_FIELD 1 +#define STV090x_WIDTH_STOP_PLL_FIELD 1 #define STV090x_OFFST_BYPASSPLLFSK_FIELD 2 #define STV090x_WIDTH_BYPASSPLLFSK_FIELD 1 #define STV090x_OFFST_SELOSCI_FIELD 1 @@ -616,7 +616,7 @@ #define STV090x_OFFST_Px_CONT_TONE_FIELD 4 #define STV090x_WIDTH_Px_CONT_TONE_FIELD 1 #define STV090x_OFFST_Px_FIFO_4BREADY_FIELD 3 -#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 2 +#define STV090x_WIDTH_Px_FIFO_4BREADY_FIELD 1 #define STV090x_OFFST_Px_FIFO_EMPTY_FIELD 2 #define STV090x_WIDTH_Px_FIFO_EMPTY_FIELD 1 #define STV090x_OFFST_Px_ABORT_DISRX_FIELD 0 @@ -847,12 +847,10 @@ #define STV090x_WIDTH_Px_DVBS2_ENABLE_FIELD 1 #define STV090x_OFFST_Px_DVBS1_ENABLE_FIELD 6 #define STV090x_WIDTH_Px_DVBS1_ENABLE_FIELD 1 -#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 5 /* check */ -#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1 -#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4 /* check */ +#define STV090x_OFFST_Px_SCAN_ENABLE_FIELD 4 #define STV090x_WIDTH_Px_SCAN_ENABLE_FIELD 1 -#define STV090x_OFFST_Px_TUN_AUTOSCAN_FIELD 3 -#define STV090x_WIDTH_Px_TUN_AUTOSCAN_FIELD 1 +#define STV090x_OFFST_Px_CFR_AUTOSCAN_FIELD 3 +#define STV090x_WIDTH_Px_CFR_AUTOSCAN_FIELD 1 #define STV090x_OFFST_Px_NOFORCE_RELOCK_FIELD 2 #define STV090x_WIDTH_Px_NOFORCE_RELOCK_FIELD 1 #define STV090x_OFFST_Px_TUN_RNG_FIELD 0 @@ -885,7 +883,7 @@ #define STV090x_P2_DMDFLYW STV090x_Px_DMDFLYW(2) #define STV090x_OFFST_Px_I2C_IRQVAL_FIELD 4 #define STV090x_WIDTH_Px_I2C_IRQVAL_FIELD 4 -#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0 /* check */ +#define STV090x_OFFST_Px_FLYWHEEL_CPT_FIELD 0 #define STV090x_WIDTH_Px_FLYWHEEL_CPT_FIELD 4 #define STV090x_Px_DSTATUS3(__x) (0xF41D - (__x - 1) * 0x200) @@ -1048,12 +1046,12 @@ #define STV090x_P1_CFRINC1 STV090x_Px_CFRINC1(1) #define STV090x_P2_CFRINC1 STV090x_Px_CFRINC1(2) #define STV090x_OFFST_Px_CFR_INC1_FIELD 0 -#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7 +#define STV090x_WIDTH_Px_CFR_INC1_FIELD 7 /* check */ #define STV090x_Px_CFRINC0(__x) (0xF44B - (__x - 1) * 0x200) #define STV090x_P1_CFRINC0 STV090x_Px_CFRINC0(1) #define STV090x_P2_CFRINC0 STV090x_Px_CFRINC0(2) -#define STV090x_OFFST_Px_CFR_INC0_FIELD 4 +#define STV090x_OFFST_Px_CFR_INC0_FIELD 4 /* check */ #define STV090x_WIDTH_Px_CFR_INC0_FIELD 4 #define STV090x_Pn_CFRy(__x, __y) (0xF44E - (__x - 1) * 0x200 - __y * 0x1) @@ -1145,14 +1143,14 @@ #define STV090x_Px_SFRINIT1(__x) (0xF45E - (__x - 1) * 0x200) #define STV090x_P1_SFRINIT1 STV090x_Px_SFRINIT1(1) #define STV090x_P2_SFRINIT1 STV090x_Px_SFRINIT1(2) -#define STV090x_OFFST_Px_SFR_INIT_FIELD 0 -#define STV090x_WIDTH_Px_SFR_INIT_FIELD 8 +#define STV090x_OFFST_Px_SFR_INIT1_FIELD 0 +#define STV090x_WIDTH_Px_SFR_INIT1_FIELD 7 #define STV090x_Px_SFRINIT0(__x) (0xF45F - (__x - 1) * 0x200) #define STV090x_P1_SFRINIT0 STV090x_Px_SFRINIT0(1) #define STV090x_P2_SFRINIT0 STV090x_Px_SFRINIT0(2) -#define STV090x_OFFST_Px_SFR_INIT_FIELD 0 -#define STV090x_WIDTH_Px_SFR_INIT_FIELD 8 +#define STV090x_OFFST_Px_SFR_INIT0_FIELD 0 +#define STV090x_WIDTH_Px_SFR_INIT0_FIELD 8 #define STV090x_Px_SFRUP1(__x) (0xF460 - (__x - 1) * 0x200) #define STV090x_P1_SFRUP1 STV090x_Px_SFRUP1(1) @@ -1178,7 +1176,7 @@ #define STV090x_OFFST_Px_SYMB_FREQ_LOW0_FIELD 0 #define STV090x_WIDTH_Px_SYMB_FREQ_LOW0_FIELD 8 -#define STV090x_Px_SFRy(__x, __y) (0xF464 - (__x-1) * 0x200 + (3 - __y)) +#define STV090x_Px_SFRy(__x, __y) (0xF467 - (__x-1) * 0x200 - __y) #define STV090x_P1_SFR0 STV090x_Px_SFRy(1, 0) #define STV090x_P1_SFR1 STV090x_Px_SFRy(1, 1) #define STV090x_P1_SFR2 STV090x_Px_SFRy(1, 2) @@ -1188,7 +1186,7 @@ #define STV090x_P2_SFR2 STV090x_Px_SFRy(2, 2) #define STV090x_P2_SFR3 STV090x_Px_SFRy(2, 3) #define STV090x_OFFST_Px_SYMB_FREQ_FIELD 0 -#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 32 +#define STV090x_WIDTH_Px_SYMB_FREQ_FIELD 8 #define STV090x_Px_TMGREG2(__x) (0xF468 - (__x - 1) * 0x200) #define STV090x_P1_TMGREG2 STV090x_Px_TMGREG2(1) @@ -1198,7 +1196,7 @@ #define STV090x_Px_TMGREG1(__x) (0xF469 - (__x - 1) * 0x200) #define STV090x_P1_TMGREG1 STV090x_Px_TMGREG1(1) -#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2) +#define STV090x_P2_TMGREG1 STV090x_Px_TMGREG1(2) #define STV090x_OFFST_Px_TMGREG_FIELD 0 #define STV090x_WIDTH_Px_TMGREG_FIELD 8 @@ -1230,7 +1228,7 @@ #define STV090x_OFFST_Px_MU_EQUALDFE_FIELD 0 #define STV090x_WIDTH_Px_MU_EQUALDFE_FIELD 3 -#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x - 1) * 0x200 + (__y - 1)) +#define STV090x_Px_EQUAIy(__x, __y) (0xf470 - (__x-1) * 0x200 + 2 * (__y-1)) #define STV090x_P1_EQUAI1 STV090x_Px_EQUAIy(1, 1) #define STV090x_P1_EQUAI2 STV090x_Px_EQUAIy(1, 2) #define STV090x_P1_EQUAI3 STV090x_Px_EQUAIy(1, 3) @@ -1251,7 +1249,7 @@ #define STV090x_OFFST_Px_EQUA_ACCIy_FIELD 0 #define STV090x_WIDTH_Px_EQUA_ACCIy_FIELD 8 -#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x - 1) * 0x200 + (__y - 1)) +#define STV090x_Px_EQUAQy(__x, __y) (0xf471 - (__x-1) * 0x200 + 2 * (__y-1)) #define STV090x_P1_EQUAQ1 STV090x_Px_EQUAQy(1, 1) #define STV090x_P1_EQUAQ2 STV090x_Px_EQUAQy(1, 2) #define STV090x_P1_EQUAQ3 STV090x_Px_EQUAQy(1, 3) @@ -1390,7 +1388,7 @@ #define STV090x_OFFST_Px_CAR2S2_16A_ALPH_E_FIELD 0 #define STV090x_WIDTH_Px_CAR2S2_16A_ALPH_E_FIELD 4 -#define STV090x_Px_ACLC2S232A(__x) (0xf499 - (__x - 1) * 0x200) +#define STV090x_Px_ACLC2S232A(__x) (0xf49A - (__x - 1) * 0x200) #define STV090x_P1_ACLC2S232A STV090x_Px_ACLC2S232A(1) #define STV090x_P2_ACLC2S232A STV090x_Px_ACLC2S232A(2) #define STV090x_OFFST_Px_CAR2S2_32A_ALPH_M_FIELD 4 @@ -1414,7 +1412,7 @@ #define STV090x_OFFST_Px_CAR2S2_8_BETA_E_FIELD 0 #define STV090x_WIDTH_Px_CAR2S2_8_BETA_E_FIELD 4 -#define STV090x_Px_BCLC2S216A(__x) (0xf49d - (__x - 1) * 0x200) +#define STV090x_Px_BCLC2S216A(__x) (0xf49e - (__x - 1) * 0x200) #define STV090x_P1_BCLC2S216A STV090x_Px_BCLC2S216A(1) #define STV090x_P2_BCLC2S216A STV090x_Px_BCLC2S216A(1) #define STV090x_OFFST_Px_CAR2S2_16A_BETA_M_FIELD 4 @@ -1422,7 +1420,7 @@ #define STV090x_OFFST_Px_CAR2S2_16A_BETA_E_FIELD 0 #define STV090x_WIDTH_Px_CAR2S2_16A_BETA_E_FIELD 4 -#define STV090x_Px_BCLC2S232A(__x) (0xf49d - (__x - 1) * 0x200) +#define STV090x_Px_BCLC2S232A(__x) (0xf49f - (__x - 1) * 0x200) #define STV090x_P1_BCLC2S232A STV090x_Px_BCLC2S232A(1) #define STV090x_P2_BCLC2S232A STV090x_Px_BCLC2S232A(1) #define STV090x_OFFST_Px_CAR2S2_32A_BETA_M_FIELD 4 @@ -1458,7 +1456,7 @@ #define STV090x_P1_MODCODLST1 STV090x_Px_MODCODLST1(1) #define STV090x_P2_MODCODLST1 STV090x_Px_MODCODLST1(2) #define STV090x_OFFST_Px_DIS_MODCOD29_FIELD 4 -#define STV090x_WIDTH_Px_DIS_MODCOD29T_FIELD 4 +#define STV090x_WIDTH_Px_DIS_MODCOD29_FIELD 4 #define STV090x_OFFST_Px_DIS_32PSK_9_10_FIELD 0 #define STV090x_WIDTH_Px_DIS_32PSK_9_10_FIELD 4 @@ -2180,7 +2178,7 @@ #define STV090x_WIDTH_Px_TSFIFOSPEED_STORE_FIELD 1 #define STV090x_OFFST_Px_DILXX_RESET_FIELD 5 #define STV090x_WIDTH_Px_DILXX_RESET_FIELD 1 -#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 5 +#define STV090x_OFFST_Px_TSSERIAL_IMPOS_FIELD 4 #define STV090x_WIDTH_Px_TSSERIAL_IMPOS_FIELD 1 #define STV090x_OFFST_Px_SCRAMBDETECT_FIELD 1 #define STV090x_WIDTH_Px_SCRAMBDETECT_FIELD 1 @@ -2190,7 +2188,7 @@ #define STV090x_P1_TSBITRATE1 STV090x_Px_TSBITRATEy(1, 1) #define STV090x_P2_TSBITRATE0 STV090x_Px_TSBITRATEy(2, 0) #define STV090x_P2_TSBITRATE1 STV090x_Px_TSBITRATEy(2, 1) -#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 7 +#define STV090x_OFFST_Px_TSFIFO_BITRATE_FIELD 0 #define STV090x_WIDTH_Px_TSFIFO_BITRATE_FIELD 8 #define STV090x_Px_ERRCTRL1(__x) (0xF598 - (__x - 1) * 0x200) diff --git a/drivers/media/dvb/frontends/stv6110.c b/drivers/media/dvb/frontends/stv6110.c index dcf1b21ea9741..bef0cc8384712 100644 --- a/drivers/media/dvb/frontends/stv6110.c +++ b/drivers/media/dvb/frontends/stv6110.c @@ -37,6 +37,7 @@ struct stv6110_priv { u32 mclk; u8 clk_div; + u8 gain; u8 regs[8]; }; @@ -255,7 +256,7 @@ static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency) u8 ret = 0x04; u32 divider, ref, p, presc, i, result_freq, vco_freq; s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val; - s32 srate; u8 gain; + s32 srate; dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__, frequency, priv->mclk); @@ -273,15 +274,8 @@ static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency) } else srate = 15000000; - if (srate >= 15000000) - gain = 3; /* +6 dB */ - else if (srate >= 5000000) - gain = 3; /* +6 dB */ - else - gain = 3; /* +6 dB */ - priv->regs[RSTV6110_CTRL2] &= ~0x0f; - priv->regs[RSTV6110_CTRL2] |= (gain & 0x0f); + priv->regs[RSTV6110_CTRL2] |= (priv->gain & 0x0f); if (frequency <= 1023000) { p = 1; @@ -436,6 +430,7 @@ struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe, priv->i2c = i2c; priv->mclk = config->mclk; priv->clk_div = config->clk_div; + priv->gain = config->gain; memcpy(&priv->regs, ®0[1], 8); diff --git a/drivers/media/dvb/frontends/stv6110.h b/drivers/media/dvb/frontends/stv6110.h index 9db2402410f63..fe71bba6a26e0 100644 --- a/drivers/media/dvb/frontends/stv6110.h +++ b/drivers/media/dvb/frontends/stv6110.h @@ -41,6 +41,7 @@ struct stv6110_config { u8 i2c_address; u32 mclk; + u8 gain; u8 clk_div; /* divisor value for the output clock */ }; diff --git a/drivers/media/dvb/frontends/stv6110x.c b/drivers/media/dvb/frontends/stv6110x.c index 3d8a2e01c9c45..bcfcb652464cc 100644 --- a/drivers/media/dvb/frontends/stv6110x.c +++ b/drivers/media/dvb/frontends/stv6110x.c @@ -95,7 +95,7 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) { struct stv6110x_state *stv6110x = fe->tuner_priv; u32 rDiv, divider; - s32 pVal, pCalc, rDivOpt = 0; + s32 pVal, pCalc, rDivOpt = 0, pCalcOpt = 1000; u8 i; STV6110x_SETFIELD(stv6110x_regs[STV6110x_CTRL1], CTRL1_K, (REFCLOCK_MHz - 16)); @@ -121,8 +121,10 @@ static int stv6110x_set_frequency(struct dvb_frontend *fe, u32 frequency) for (rDiv = 0; rDiv <= 3; rDiv++) { pCalc = (REFCLOCK_kHz / 100) / R_DIV(rDiv); - if ((abs((s32)(pCalc - pVal))) < (abs((s32)(1000 - pVal)))) + if ((abs((s32)(pCalc - pVal))) < (abs((s32)(pCalcOpt - pVal)))) rDivOpt = rDiv; + + pCalcOpt = (REFCLOCK_kHz / 100) / R_DIV(rDivOpt); } divider = (frequency * R_DIV(rDivOpt) * pVal) / REFCLOCK_kHz; diff --git a/drivers/media/dvb/pt1/pt1.c b/drivers/media/dvb/pt1/pt1.c index 1fd8306371e2b..81e623a90f09d 100644 --- a/drivers/media/dvb/pt1/pt1.c +++ b/drivers/media/dvb/pt1/pt1.c @@ -27,7 +27,6 @@ #include #include #include -#include #include "dvbdev.h" #include "dvb_demux.h" diff --git a/drivers/media/dvb/pt1/va1j5jf8007s.c b/drivers/media/dvb/pt1/va1j5jf8007s.c index 2db940f8635f5..fc6594996e794 100644 --- a/drivers/media/dvb/pt1/va1j5jf8007s.c +++ b/drivers/media/dvb/pt1/va1j5jf8007s.c @@ -48,6 +48,60 @@ struct va1j5jf8007s_state { enum va1j5jf8007s_tune_state tune_state; }; +static int va1j5jf8007s_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct va1j5jf8007s_state *state; + u8 addr; + int i; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + s32 word, x1, x2, x3, x4, x5, y; + + state = fe->demodulator_priv; + addr = state->config->demod_address; + + word = 0; + for (i = 0; i < 2; i++) { + write_buf[0] = 0xbc + i; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + word <<= 8; + word |= read_buf[0]; + } + + word -= 3000; + if (word < 0) + word = 0; + + x1 = int_sqrt(word << 16) * ((15625ll << 21) / 1000000); + x2 = (s64)x1 * x1 >> 31; + x3 = (s64)x2 * x1 >> 31; + x4 = (s64)x2 * x2 >> 31; + x5 = (s64)x4 * x1 >> 31; + + y = (58857ll << 23) / 1000; + y -= (s64)x1 * ((89565ll << 24) / 1000) >> 30; + y += (s64)x2 * ((88977ll << 24) / 1000) >> 28; + y -= (s64)x3 * ((50259ll << 25) / 1000) >> 27; + y += (s64)x4 * ((14341ll << 27) / 1000) >> 27; + y -= (s64)x5 * ((16346ll << 30) / 10000) >> 28; + + *snr = y < 0 ? 0 : y >> 15; + return 0; +} + static int va1j5jf8007s_get_frontend_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; @@ -337,7 +391,7 @@ va1j5jf8007s_tune(struct dvb_frontend *fe, { struct va1j5jf8007s_state *state; int ret; - int lock; + int lock = 0; state = fe->demodulator_priv; @@ -536,6 +590,7 @@ static struct dvb_frontend_ops va1j5jf8007s_ops = { FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, }, + .read_snr = va1j5jf8007s_read_snr, .get_frontend_algo = va1j5jf8007s_get_frontend_algo, .read_status = va1j5jf8007s_read_status, .tune = va1j5jf8007s_tune, diff --git a/drivers/media/dvb/pt1/va1j5jf8007t.c b/drivers/media/dvb/pt1/va1j5jf8007t.c index 71117f4ca7e6d..3db4f3e34e8f4 100644 --- a/drivers/media/dvb/pt1/va1j5jf8007t.c +++ b/drivers/media/dvb/pt1/va1j5jf8007t.c @@ -46,6 +46,52 @@ struct va1j5jf8007t_state { enum va1j5jf8007t_tune_state tune_state; }; +static int va1j5jf8007t_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct va1j5jf8007t_state *state; + u8 addr; + int i; + u8 write_buf[1], read_buf[1]; + struct i2c_msg msgs[2]; + s32 word, x, y; + + state = fe->demodulator_priv; + addr = state->config->demod_address; + + word = 0; + for (i = 0; i < 3; i++) { + write_buf[0] = 0x8b + i; + + msgs[0].addr = addr; + msgs[0].flags = 0; + msgs[0].len = sizeof(write_buf); + msgs[0].buf = write_buf; + + msgs[1].addr = addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(read_buf); + msgs[1].buf = read_buf; + + if (i2c_transfer(state->adap, msgs, 2) != 2) + return -EREMOTEIO; + + word <<= 8; + word |= read_buf[0]; + } + + if (!word) + return -EIO; + + x = 10 * (intlog10(0x540000 * 100 / word) - (2 << 24)); + y = (24ll << 46) / 1000000; + y = ((s64)y * x >> 30) - (16ll << 40) / 10000; + y = ((s64)y * x >> 29) + (398ll << 35) / 10000; + y = ((s64)y * x >> 30) + (5491ll << 29) / 10000; + y = ((s64)y * x >> 30) + (30965ll << 23) / 10000; + *snr = y >> 15; + return 0; +} + static int va1j5jf8007t_get_frontend_algo(struct dvb_frontend *fe) { return DVBFE_ALGO_HW; @@ -224,7 +270,7 @@ va1j5jf8007t_tune(struct dvb_frontend *fe, { struct va1j5jf8007t_state *state; int ret; - int lock, retry; + int lock = 0, retry = 0; state = fe->demodulator_priv; @@ -393,6 +439,7 @@ static struct dvb_frontend_ops va1j5jf8007t_ops = { FE_CAN_GUARD_INTERVAL_AUTO | FE_CAN_HIERARCHY_AUTO, }, + .read_snr = va1j5jf8007t_read_snr, .get_frontend_algo = va1j5jf8007t_get_frontend_algo, .read_status = va1j5jf8007t_read_status, .tune = va1j5jf8007t_tune, diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c index 0420e2885e752..1067b22eb0c66 100644 --- a/drivers/media/dvb/siano/sms-cards.c +++ b/drivers/media/dvb/siano/sms-cards.c @@ -97,7 +97,7 @@ static struct sms_board sms_boards[] = { }, }; -struct sms_board *sms_get_board(int id) +struct sms_board *sms_get_board(unsigned id) { BUG_ON(id >= ARRAY_SIZE(sms_boards)); @@ -294,6 +294,8 @@ int sms_board_load_modules(int id) case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A: case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B: case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD: + case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2: request_module("smsdvb"); break; default: diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h index 38f062f6ad68a..8f19fc000b469 100644 --- a/drivers/media/dvb/siano/sms-cards.h +++ b/drivers/media/dvb/siano/sms-cards.h @@ -81,7 +81,7 @@ struct sms_board { int led_power, led_hi, led_lo, lna_ctrl, rf_switch; }; -struct sms_board *sms_get_board(int id); +struct sms_board *sms_get_board(unsigned id); extern struct smscore_device_t *coredev; diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c index fa6a62369a783..ca758bcb48c95 100644 --- a/drivers/media/dvb/siano/smscoreapi.c +++ b/drivers/media/dvb/siano/smscoreapi.c @@ -1373,7 +1373,7 @@ static int GetGpioPinParams(u32 PinNum, u32 *pTranslatedPinNum, *pGroupCfg = 1; - if (PinNum >= 0 && PinNum <= 1) { + if (PinNum <= 1) { *pTranslatedPinNum = 0; *pGroupNum = 9; *pGroupCfg = 2; diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c index d1d652e7f8905..24206cbda2646 100644 --- a/drivers/media/dvb/siano/smssdio.c +++ b/drivers/media/dvb/siano/smssdio.c @@ -78,7 +78,7 @@ struct smssdio_device { static int smssdio_sendrequest(void *context, void *buffer, size_t size) { - int ret; + int ret = 0; struct smssdio_device *smsdev; smsdev = context; diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 8ea9152276744..983672aa24505 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -1055,7 +1055,7 @@ static const struct stb0899_s1_reg knc1_stb0899_s1_init_3[] = { { STB0899_TSCFGH , 0x0c }, { STB0899_TSCFGM , 0x00 }, { STB0899_TSCFGL , 0x0c }, - { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */ + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ { STB0899_RSSYNCDEL , 0x00 }, { STB0899_TSINHDELH , 0x02 }, { STB0899_TSINHDELM , 0x00 }, diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index b5c681372b6ca..7d193ebc0aea3 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -178,7 +178,7 @@ static void msp430_ir_interrupt(unsigned long data) if (budget_ci->ir.last_raw != raw || !timer_pending(&budget_ci->ir.timer_keyup)) { ir_input_nokey(dev, &budget_ci->ir.state); ir_input_keydown(dev, &budget_ci->ir.state, - budget_ci->ir.ir_key, raw); + budget_ci->ir.ir_key); budget_ci->ir.last_raw = raw; } @@ -224,8 +224,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x1011: case 0x1012: /* The hauppauge keymap is a superset of these remotes */ - ir_input_init(input_dev, &budget_ci->ir.state, + error = ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5, &ir_codes_hauppauge_new_table); + if (error < 0) + goto out2; if (rc5_device < 0) budget_ci->ir.rc5_device = 0x1f; @@ -236,8 +238,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci) case 0x1017: case 0x101a: /* for the Technotrend 1500 bundled remote */ - ir_input_init(input_dev, &budget_ci->ir.state, + error = ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5, &ir_codes_tt_1500_table); + if (error < 0) + goto out2; if (rc5_device < 0) budget_ci->ir.rc5_device = IR_DEVICE_ANY; @@ -246,8 +250,10 @@ static int msp430_ir_init(struct budget_ci *budget_ci) break; default: /* unknown remote */ - ir_input_init(input_dev, &budget_ci->ir.state, + error = ir_input_init(input_dev, &budget_ci->ir.state, IR_TYPE_RC5, &ir_codes_budget_ci_old_table); + if (error < 0) + goto out2; if (rc5_device < 0) budget_ci->ir.rc5_device = IR_DEVICE_ANY; @@ -280,6 +286,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci) return 0; out2: + ir_input_free(input_dev); input_free_device(input_dev); out1: return error; @@ -297,6 +304,7 @@ static void msp430_ir_deinit(struct budget_ci *budget_ci) del_timer_sync(&dev->timer); ir_input_nokey(dev, &budget_ci->ir.state); + ir_input_free(dev); input_unregister_device(dev); } @@ -1248,7 +1256,7 @@ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_3[] = { { STB0899_TSCFGH , 0x0c }, { STB0899_TSCFGM , 0x00 }, { STB0899_TSCFGL , 0x0c }, - { STB0899_TSOUT , 0x0d }, /* 0x0d for CAM */ + { STB0899_TSOUT , 0x4d }, /* 0x0d for CAM */ { STB0899_RSSYNCDEL , 0x00 }, { STB0899_TSINHDELH , 0x02 }, { STB0899_TSINHDELM , 0x00 }, diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c index d91e0638448fc..53baccbab17f0 100644 --- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c +++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c @@ -1198,7 +1198,7 @@ static int ttusb_init_rc( struct ttusb_dec *dec) int err; usb_make_path(dec->udev, dec->rc_phys, sizeof(dec->rc_phys)); - strlcpy(dec->rc_phys, "/input0", sizeof(dec->rc_phys)); + strlcat(dec->rc_phys, "/input0", sizeof(dec->rc_phys)); input_dev = input_allocate_device(); if (!input_dev) diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index b134553eb3b50..d4389963b3120 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -419,4 +419,16 @@ config RADIO_TEA5764_XTAL Say Y here if TEA5764 have a 32768 Hz crystal in circuit, say N here if TEA5764 reference frequency is connected in FREQIN. +config RADIO_TEF6862 + tristate "TEF6862 Car Radio Enhanced Selectivity Tuner" + depends on I2C && VIDEO_V4L2 + ---help--- + Say Y here if you want to use the TEF6862 Car Radio Enhanced + Selectivity Tuner, found for instance on the Russellville development + board. On the russellville the device is connected to internal + timberdale I2C bus. + + To compile this driver as a module, choose M here: the + module will be called TEF6862. + endif # RADIO_ADAPTERS diff --git a/drivers/media/radio/Makefile b/drivers/media/radio/Makefile index 8a63d543ae417..01922ada6914f 100644 --- a/drivers/media/radio/Makefile +++ b/drivers/media/radio/Makefile @@ -23,5 +23,6 @@ obj-$(CONFIG_USB_DSBR) += dsbr100.o obj-$(CONFIG_RADIO_SI470X) += si470x/ obj-$(CONFIG_USB_MR800) += radio-mr800.o obj-$(CONFIG_RADIO_TEA5764) += radio-tea5764.o +obj-$(CONFIG_RADIO_TEF6862) += tef6862.o EXTRA_CFLAGS += -Isound diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index 5f79acb56e48e..949f60513d9e0 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -85,6 +85,9 @@ MODULE_LICENSE("GPL"); #define amradio_dev_warn(dev, fmt, arg...) \ dev_warn(dev, MR800_DRIVER_NAME " - " fmt, ##arg) +#define amradio_dev_err(dev, fmt, arg...) \ + dev_err(dev, MR800_DRIVER_NAME " - " fmt, ##arg) + /* Probably USB_TIMEOUT should be modified in module parameter */ #define BUFFER_LENGTH 8 #define USB_TIMEOUT 500 @@ -129,18 +132,20 @@ static int usb_amradio_resume(struct usb_interface *intf); struct amradio_device { /* reference to USB and video device */ struct usb_device *usbdev; - struct video_device *videodev; + struct usb_interface *intf; + struct video_device videodev; struct v4l2_device v4l2_dev; unsigned char *buffer; struct mutex lock; /* buffer locking */ int curfreq; int stereo; - int users; - int removed; int muted; + int initialized; }; +#define vdev_to_amradio(r) container_of(r, struct amradio_device, videodev) + /* USB Device ID List */ static struct usb_device_id usb_amradio_device_table[] = { {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT, @@ -159,7 +164,7 @@ static struct usb_driver usb_amradio_driver = { .resume = usb_amradio_resume, .reset_resume = usb_amradio_resume, .id_table = usb_amradio_device_table, - .supports_autosuspend = 0, + .supports_autosuspend = 1, }; /* switch on/off the radio. Send 8 bytes to device */ @@ -168,11 +173,7 @@ static int amradio_set_mute(struct amradio_device *radio, char argument) int retval; int size; - /* safety check */ - if (radio->removed) - return -EIO; - - mutex_lock(&radio->lock); + BUG_ON(!mutex_is_locked(&radio->lock)); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; @@ -187,14 +188,12 @@ static int amradio_set_mute(struct amradio_device *radio, char argument) (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) { - mutex_unlock(&radio->lock); + amradio_dev_warn(&radio->videodev.dev, "set mute failed\n"); return retval; } radio->muted = argument; - mutex_unlock(&radio->lock); - return retval; } @@ -205,11 +204,7 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) int size; unsigned short freq_send = 0x10 + (freq >> 3) / 25; - /* safety check */ - if (radio->removed) - return -EIO; - - mutex_lock(&radio->lock); + BUG_ON(!mutex_is_locked(&radio->lock)); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; @@ -223,10 +218,8 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); - if (retval < 0 || size != BUFFER_LENGTH) { - mutex_unlock(&radio->lock); - return retval; - } + if (retval < 0 || size != BUFFER_LENGTH) + goto out_err; /* frequency is calculated from freq_send and placed in first 2 bytes */ radio->buffer[0] = (freq_send >> 8) & 0xff; @@ -240,13 +233,15 @@ static int amradio_setfreq(struct amradio_device *radio, int freq) retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); - if (retval < 0 || size != BUFFER_LENGTH) { - mutex_unlock(&radio->lock); - return retval; - } + if (retval < 0 || size != BUFFER_LENGTH) + goto out_err; - mutex_unlock(&radio->lock); + radio->curfreq = freq; + goto out; +out_err: + amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n"); +out: return retval; } @@ -255,11 +250,7 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument) int retval; int size; - /* safety check */ - if (radio->removed) - return -EIO; - - mutex_lock(&radio->lock); + BUG_ON(!mutex_is_locked(&radio->lock)); radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; @@ -274,14 +265,14 @@ static int amradio_set_stereo(struct amradio_device *radio, char argument) (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) { - radio->stereo = -1; - mutex_unlock(&radio->lock); + amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n"); return retval; } - radio->stereo = 1; - - mutex_unlock(&radio->lock); + if (argument == WANT_STEREO) + radio->stereo = 1; + else + radio->stereo = 0; return retval; } @@ -296,19 +287,19 @@ static void usb_amradio_disconnect(struct usb_interface *intf) struct amradio_device *radio = usb_get_intfdata(intf); mutex_lock(&radio->lock); - radio->removed = 1; + radio->usbdev = NULL; mutex_unlock(&radio->lock); usb_set_intfdata(intf, NULL); - video_unregister_device(radio->videodev); v4l2_device_disconnect(&radio->v4l2_dev); + video_unregister_device(&radio->videodev); } /* vidioc_querycap - query device capabilities */ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { - struct amradio_device *radio = video_drvdata(file); + struct amradio_device *radio = file->private_data; strlcpy(v->driver, "radio-mr800", sizeof(v->driver)); strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card)); @@ -322,13 +313,9 @@ static int vidioc_querycap(struct file *file, void *priv, static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); + struct amradio_device *radio = file->private_data; int retval; - /* safety check */ - if (radio->removed) - return -EIO; - if (v->index > 0) return -EINVAL; @@ -341,9 +328,6 @@ static int vidioc_g_tuner(struct file *file, void *priv, * amradio_set_stereo shouldn't be here */ retval = amradio_set_stereo(radio, WANT_STEREO); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set stereo failed\n"); strcpy(v->name, "FM"); v->type = V4L2_TUNER_RADIO; @@ -357,19 +341,16 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->audmode = V4L2_TUNER_MODE_MONO; v->signal = 0xffff; /* Can't get the signal strength, sad.. */ v->afc = 0; /* Don't know what is this */ - return 0; + + return retval; } /* vidioc_s_tuner - set tuner attributes */ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - int retval; - - /* safety check */ - if (radio->removed) - return -EIO; + struct amradio_device *radio = file->private_data; + int retval = -EINVAL; if (v->index > 0) return -EINVAL; @@ -378,57 +359,33 @@ static int vidioc_s_tuner(struct file *file, void *priv, switch (v->audmode) { case V4L2_TUNER_MODE_MONO: retval = amradio_set_stereo(radio, WANT_MONO); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set mono failed\n"); break; case V4L2_TUNER_MODE_STEREO: retval = amradio_set_stereo(radio, WANT_STEREO); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set stereo failed\n"); break; - default: - return -EINVAL; } - return 0; + return retval; } /* vidioc_s_frequency - set tuner radio frequency */ static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - int retval; + struct amradio_device *radio = file->private_data; - /* safety check */ - if (radio->removed) - return -EIO; - - mutex_lock(&radio->lock); - radio->curfreq = f->frequency; - mutex_unlock(&radio->lock); - - retval = amradio_setfreq(radio, radio->curfreq); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set frequency failed\n"); - return 0; + return amradio_setfreq(radio, f->frequency); } /* vidioc_g_frequency - get tuner radio frequency */ static int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - - /* safety check */ - if (radio->removed) - return -EIO; + struct amradio_device *radio = file->private_data; f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; + return 0; } @@ -448,17 +405,14 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - - /* safety check */ - if (radio->removed) - return -EIO; + struct amradio_device *radio = file->private_data; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: ctrl->value = radio->muted; return 0; } + return -EINVAL; } @@ -466,33 +420,20 @@ static int vidioc_g_ctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - int retval; - - /* safety check */ - if (radio->removed) - return -EIO; + struct amradio_device *radio = file->private_data; + int retval = -EINVAL; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { + if (ctrl->value) retval = amradio_set_mute(radio, AMRADIO_STOP); - if (retval < 0) { - amradio_dev_warn(&radio->videodev->dev, - "amradio_stop failed\n"); - return -1; - } - } else { + else retval = amradio_set_mute(radio, AMRADIO_START); - if (retval < 0) { - amradio_dev_warn(&radio->videodev->dev, - "amradio_start failed\n"); - return -1; - } - } - return 0; + + break; } - return -EINVAL; + + return retval; } /* vidioc_g_audio - get audio attributes */ @@ -531,75 +472,108 @@ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) return 0; } -/* open device - amradio_start() and amradio_setfreq() */ -static int usb_amradio_open(struct file *file) +static int usb_amradio_init(struct amradio_device *radio) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); int retval; - lock_kernel(); + retval = amradio_set_mute(radio, AMRADIO_STOP); + if (retval) + goto out_err; - radio->users = 1; - radio->muted = 1; + retval = amradio_set_stereo(radio, WANT_STEREO); + if (retval) + goto out_err; - retval = amradio_set_mute(radio, AMRADIO_START); - if (retval < 0) { - amradio_dev_warn(&radio->videodev->dev, - "radio did not start up properly\n"); - radio->users = 0; - unlock_kernel(); - return -EIO; + radio->initialized = 1; + goto out; + +out_err: + amradio_dev_err(&radio->videodev.dev, "initialization failed\n"); +out: + return retval; +} + +/* open device - amradio_start() and amradio_setfreq() */ +static int usb_amradio_open(struct file *file) +{ + struct amradio_device *radio = vdev_to_amradio(video_devdata(file)); + int retval = 0; + + mutex_lock(&radio->lock); + + if (!radio->usbdev) { + retval = -EIO; + goto unlock; } - retval = amradio_set_stereo(radio, WANT_STEREO); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set stereo failed\n"); + file->private_data = radio; + retval = usb_autopm_get_interface(radio->intf); + if (retval) + goto unlock; - retval = amradio_setfreq(radio, radio->curfreq); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "set frequency failed\n"); + if (unlikely(!radio->initialized)) { + retval = usb_amradio_init(radio); + if (retval) + usb_autopm_put_interface(radio->intf); + } - unlock_kernel(); - return 0; +unlock: + mutex_unlock(&radio->lock); + return retval; } /*close device */ static int usb_amradio_close(struct file *file) { - struct amradio_device *radio = video_get_drvdata(video_devdata(file)); - int retval; - - if (!radio) - return -ENODEV; + struct amradio_device *radio = file->private_data; + int retval = 0; mutex_lock(&radio->lock); - radio->users = 0; + + if (!radio->usbdev) + retval = -EIO; + else + usb_autopm_put_interface(radio->intf); + mutex_unlock(&radio->lock); + return retval; +} - if (!radio->removed) { - retval = amradio_set_mute(radio, AMRADIO_STOP); - if (retval < 0) - amradio_dev_warn(&radio->videodev->dev, - "amradio_stop failed\n"); +static long usb_amradio_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct amradio_device *radio = file->private_data; + long retval = 0; + + mutex_lock(&radio->lock); + + if (!radio->usbdev) { + retval = -EIO; + goto unlock; } - return 0; + retval = video_ioctl2(file, cmd, arg); + +unlock: + mutex_unlock(&radio->lock); + return retval; } /* Suspend device - stop device. Need to be checked and fixed */ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) { struct amradio_device *radio = usb_get_intfdata(intf); - int retval; - retval = amradio_set_mute(radio, AMRADIO_STOP); - if (retval < 0) - dev_warn(&intf->dev, "amradio_stop failed\n"); + mutex_lock(&radio->lock); + + if (!radio->muted && radio->initialized) { + amradio_set_mute(radio, AMRADIO_STOP); + radio->muted = 0; + } dev_info(&intf->dev, "going into suspend..\n"); + mutex_unlock(&radio->lock); return 0; } @@ -607,14 +581,26 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message) static int usb_amradio_resume(struct usb_interface *intf) { struct amradio_device *radio = usb_get_intfdata(intf); - int retval; - retval = amradio_set_mute(radio, AMRADIO_START); - if (retval < 0) - dev_warn(&intf->dev, "amradio_start failed\n"); + mutex_lock(&radio->lock); + + if (unlikely(!radio->initialized)) + goto unlock; + + if (radio->stereo) + amradio_set_stereo(radio, WANT_STEREO); + else + amradio_set_stereo(radio, WANT_MONO); + + amradio_setfreq(radio, radio->curfreq); + if (!radio->muted) + amradio_set_mute(radio, AMRADIO_START); + +unlock: dev_info(&intf->dev, "coming out of suspend..\n"); + mutex_unlock(&radio->lock); return 0; } @@ -623,7 +609,7 @@ static const struct v4l2_file_operations usb_amradio_fops = { .owner = THIS_MODULE, .open = usb_amradio_open, .release = usb_amradio_close, - .ioctl = video_ioctl2, + .ioctl = usb_amradio_ioctl, }; static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { @@ -643,10 +629,7 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { static void usb_amradio_video_device_release(struct video_device *videodev) { - struct amradio_device *radio = video_get_drvdata(videodev); - - /* we call v4l to free radio->videodev */ - video_device_release(videodev); + struct amradio_device *radio = vdev_to_amradio(videodev); v4l2_device_unregister(&radio->v4l2_dev); @@ -660,70 +643,63 @@ static int usb_amradio_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct amradio_device *radio; - struct v4l2_device *v4l2_dev; - int retval; + int retval = 0; radio = kzalloc(sizeof(struct amradio_device), GFP_KERNEL); if (!radio) { dev_err(&intf->dev, "kmalloc for amradio_device failed\n"); - return -ENOMEM; + retval = -ENOMEM; + goto err; } radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL); if (!radio->buffer) { dev_err(&intf->dev, "kmalloc for radio->buffer failed\n"); - kfree(radio); - return -ENOMEM; + retval = -ENOMEM; + goto err_nobuf; } - v4l2_dev = &radio->v4l2_dev; - retval = v4l2_device_register(&intf->dev, v4l2_dev); + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); if (retval < 0) { dev_err(&intf->dev, "couldn't register v4l2_device\n"); - kfree(radio->buffer); - kfree(radio); - return retval; + goto err_v4l2; } - radio->videodev = video_device_alloc(); - - if (!radio->videodev) { - dev_err(&intf->dev, "video_device_alloc failed\n"); - kfree(radio->buffer); - kfree(radio); - return -ENOMEM; - } + strlcpy(radio->videodev.name, radio->v4l2_dev.name, + sizeof(radio->videodev.name)); + radio->videodev.v4l2_dev = &radio->v4l2_dev; + radio->videodev.fops = &usb_amradio_fops; + radio->videodev.ioctl_ops = &usb_amradio_ioctl_ops; + radio->videodev.release = usb_amradio_video_device_release; - strlcpy(radio->videodev->name, v4l2_dev->name, sizeof(radio->videodev->name)); - radio->videodev->v4l2_dev = v4l2_dev; - radio->videodev->fops = &usb_amradio_fops; - radio->videodev->ioctl_ops = &usb_amradio_ioctl_ops; - radio->videodev->release = usb_amradio_video_device_release; - - radio->removed = 0; - radio->users = 0; radio->usbdev = interface_to_usbdev(intf); + radio->intf = intf; radio->curfreq = 95.16 * FREQ_MUL; - radio->stereo = -1; mutex_init(&radio->lock); - video_set_drvdata(radio->videodev, radio); + video_set_drvdata(&radio->videodev, radio); - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr); + retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, + radio_nr); if (retval < 0) { dev_err(&intf->dev, "could not register video device\n"); - video_device_release(radio->videodev); - v4l2_device_unregister(v4l2_dev); - kfree(radio->buffer); - kfree(radio); - return -EIO; + goto err_vdev; } usb_set_intfdata(intf, radio); return 0; + +err_vdev: + v4l2_device_unregister(&radio->v4l2_dev); +err_v4l2: + kfree(radio->buffer); +err_nobuf: + kfree(radio); +err: + return retval; } static int __init amradio_init(void) diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c new file mode 100644 index 0000000000000..6e607ff0c1699 --- /dev/null +++ b/drivers/media/radio/tef6862.c @@ -0,0 +1,232 @@ +/* + * tef6862.c Philips TEF6862 Car Radio Enhanced Selectivity Tuner + * Copyright (c) 2009 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "tef6862" + +#define FREQ_MUL 16000 + +#define TEF6862_LO_FREQ (875 * FREQ_MUL / 10) +#define TEF6862_HI_FREQ (108 * FREQ_MUL) + +/* Write mode sub addresses */ +#define WM_SUB_BANDWIDTH 0x0 +#define WM_SUB_PLLM 0x1 +#define WM_SUB_PLLL 0x2 +#define WM_SUB_DAA 0x3 +#define WM_SUB_AGC 0x4 +#define WM_SUB_BAND 0x5 +#define WM_SUB_CONTROL 0x6 +#define WM_SUB_LEVEL 0x7 +#define WM_SUB_IFCF 0x8 +#define WM_SUB_IFCAP 0x9 +#define WM_SUB_ACD 0xA +#define WM_SUB_TEST 0xF + +/* Different modes of the MSA register */ +#define MODE_BUFFER 0x0 +#define MODE_PRESET 0x1 +#define MODE_SEARCH 0x2 +#define MODE_AF_UPDATE 0x3 +#define MODE_JUMP 0x4 +#define MODE_CHECK 0x5 +#define MODE_LOAD 0x6 +#define MODE_END 0x7 +#define MODE_SHIFT 5 + +struct tef6862_state { + struct v4l2_subdev sd; + unsigned long freq; +}; + +static inline struct tef6862_state *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct tef6862_state, sd); +} + +static u16 tef6862_sigstr(struct i2c_client *client) +{ + u8 buf[4]; + int err = i2c_master_recv(client, buf, sizeof(buf)); + if (err == sizeof(buf)) + return buf[3] << 8; + return 0; +} + +static int tef6862_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +{ + if (v->index > 0) + return -EINVAL; + + /* only support FM for now */ + strlcpy(v->name, "FM", sizeof(v->name)); + v->type = V4L2_TUNER_RADIO; + v->rangelow = TEF6862_LO_FREQ; + v->rangehigh = TEF6862_HI_FREQ; + v->rxsubchans = V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW; + v->audmode = V4L2_TUNER_MODE_STEREO; + v->signal = tef6862_sigstr(v4l2_get_subdevdata(sd)); + + return 0; +} + +static int tef6862_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *v) +{ + return v->index ? -EINVAL : 0; +} + +static int tef6862_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tef6862_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 pll; + u8 i2cmsg[3]; + int err; + + if (f->tuner != 0) + return -EINVAL; + + pll = 1964 + ((f->frequency - TEF6862_LO_FREQ) * 20) / FREQ_MUL; + i2cmsg[0] = (MODE_PRESET << MODE_SHIFT) | WM_SUB_PLLM; + i2cmsg[1] = (pll >> 8) & 0xff; + i2cmsg[2] = pll & 0xff; + + err = i2c_master_send(client, i2cmsg, sizeof(i2cmsg)); + if (!err) + state->freq = f->frequency; + return err; +} + +static int tef6862_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f) +{ + struct tef6862_state *state = to_state(sd); + + if (f->tuner != 0) + return -EINVAL; + f->type = V4L2_TUNER_RADIO; + f->frequency = state->freq; + return 0; +} + +static int tef6862_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEF6862, 0); +} + +static const struct v4l2_subdev_tuner_ops tef6862_tuner_ops = { + .g_tuner = tef6862_g_tuner, + .s_tuner = tef6862_s_tuner, + .s_frequency = tef6862_s_frequency, + .g_frequency = tef6862_g_frequency, +}; + +static const struct v4l2_subdev_core_ops tef6862_core_ops = { + .g_chip_ident = tef6862_g_chip_ident, +}; + +static const struct v4l2_subdev_ops tef6862_ops = { + .core = &tef6862_core_ops, + .tuner = &tef6862_tuner_ops, +}; + +/* + * Generic i2c probe + * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' + */ + +static int __devinit tef6862_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct tef6862_state *state; + struct v4l2_subdev *sd; + + /* Check if the adapter supports the needed features */ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -EIO; + + v4l_info(client, "chip found @ 0x%02x (%s)\n", + client->addr << 1, client->adapter->name); + + state = kmalloc(sizeof(struct tef6862_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + state->freq = TEF6862_LO_FREQ; + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &tef6862_ops); + + return 0; +} + +static int __devexit tef6862_remove(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); + return 0; +} + +static const struct i2c_device_id tef6862_id[] = { + {DRIVER_NAME, 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tef6862_id); + +static struct i2c_driver tef6862_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, + .probe = tef6862_probe, + .remove = tef6862_remove, + .id_table = tef6862_id, +}; + +static __init int tef6862_init(void) +{ + return i2c_add_driver(&tef6862_driver); +} + +static __exit void tef6862_exit(void) +{ + i2c_del_driver(&tef6862_driver); +} + +module_init(tef6862_init); +module_exit(tef6862_exit); + +MODULE_DESCRIPTION("TEF6862 Car Radio Enhanced Selectivity Tuner"); +MODULE_AUTHOR("Mocean Laboratories"); +MODULE_LICENSE("GPL v2"); + diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index e6186b338a12d..9dc74c93bf245 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -600,7 +600,7 @@ source "drivers/media/video/bt8xx/Kconfig" config VIDEO_PMS tristate "Mediavision Pro Movie Studio Video For Linux" - depends on ISA && VIDEO_V4L1 + depends on ISA && VIDEO_V4L2 help Say Y if you have such a thing. @@ -847,6 +847,12 @@ config SOC_CAMERA_MT9V022 help This driver supports MT9V022 cameras from Micron +config SOC_CAMERA_RJ54N1 + tristate "rj54n1cb0c support" + depends on SOC_CAMERA && I2C + help + This is a rj54n1cb0c video driver + config SOC_CAMERA_TW9910 tristate "tw9910 support" depends on SOC_CAMERA && I2C @@ -865,6 +871,12 @@ config SOC_CAMERA_OV772X help This is a ov772x camera driver +config SOC_CAMERA_OV9640 + tristate "ov9640 camera support" + depends on SOC_CAMERA && I2C + help + This is a ov9640 camera driver + config MX1_VIDEO bool @@ -939,9 +951,14 @@ source "drivers/media/video/usbvideo/Kconfig" source "drivers/media/video/et61x251/Kconfig" config VIDEO_OVCAMCHIP - tristate "OmniVision Camera Chip support" + tristate "OmniVision Camera Chip support (DEPRECATED)" depends on I2C && VIDEO_V4L1 ---help--- + This driver is DEPRECATED please use the gspca ov519 module + instead. Note that for the ov511 / ov518 support of the gspca module + you need atleast version 0.6.0 of libv4l and for the w9968cf + atleast version 0.6.3 of libv4l. + Support for the OmniVision OV6xxx and OV7xxx series of camera chips. This driver is intended to be used with the ov511 and w9968cf USB camera drivers. @@ -950,9 +967,13 @@ config VIDEO_OVCAMCHIP module will be called ovcamchip. config USB_W9968CF - tristate "USB W996[87]CF JPEG Dual Mode Camera support" + tristate "USB W996[87]CF JPEG Dual Mode Camera support (DEPRECATED)" depends on VIDEO_V4L1 && I2C && VIDEO_OVCAMCHIP ---help--- + This driver is DEPRECATED please use the gspca ov519 module + instead. Note that for the w9968cf support of the gspca module + you need atleast version 0.6.3 of libv4l. + Say Y here if you want support for cameras based on OV681 or Winbond W9967CF/W9968CF JPEG USB Dual Mode Camera Chips. @@ -995,9 +1016,13 @@ config USB_SE401 source "drivers/media/video/sn9c102/Kconfig" config USB_STV680 - tristate "USB STV680 (Pencam) Camera support" + tristate "USB STV680 (Pencam) Camera support (DEPRECATED)" depends on VIDEO_V4L1 ---help--- + This driver is DEPRECATED please use the gspca stv0680 module + instead. Note that for the gspca stv0680 module you need + atleast version 0.6.3 of libv4l. + Say Y here if you want to connect this type of camera to your computer's USB port. This includes the Pencam line of cameras. See for more information diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index e541932a789bc..7a2dcc34111c4 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -77,6 +77,8 @@ obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o +obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o +obj-$(CONFIG_SOC_CAMERA_RJ54N1) += rj54n1cb0c.o obj-$(CONFIG_SOC_CAMERA_TW9910) += tw9910.o # And now the v4l2 drivers: diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c index 1b3cbd02a7fd4..0826f0dabc172 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c @@ -27,17 +27,40 @@ #include #include #include +#include #define DRIVER_NAME "adv7180" -#define ADV7180_INPUT_CONTROL_REG 0x00 -#define ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM 0x00 -#define ADV7180_AUTODETECT_ENABLE_REG 0x07 -#define ADV7180_AUTODETECT_DEFAULT 0x7f - - -#define ADV7180_STATUS1_REG 0x10 -#define ADV7180_STATUS1_AUTOD_MASK 0x70 +#define ADV7180_INPUT_CONTROL_REG 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM 0x00 +#define ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM_PED 0x10 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_J_SECAM 0x20 +#define ADV7180_INPUT_CONTROL_AD_PAL_N_NTSC_M_SECAM 0x30 +#define ADV7180_INPUT_CONTROL_NTSC_J 0x40 +#define ADV7180_INPUT_CONTROL_NTSC_M 0x50 +#define ADV7180_INPUT_CONTROL_PAL60 0x60 +#define ADV7180_INPUT_CONTROL_NTSC_443 0x70 +#define ADV7180_INPUT_CONTROL_PAL_BG 0x80 +#define ADV7180_INPUT_CONTROL_PAL_N 0x90 +#define ADV7180_INPUT_CONTROL_PAL_M 0xa0 +#define ADV7180_INPUT_CONTROL_PAL_M_PED 0xb0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N 0xc0 +#define ADV7180_INPUT_CONTROL_PAL_COMB_N_PED 0xd0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM 0xe0 +#define ADV7180_INPUT_CONTROL_PAL_SECAM_PED 0xf0 + +#define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 +#define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 + +#define ADV7180_AUTODETECT_ENABLE_REG 0x07 +#define ADV7180_AUTODETECT_DEFAULT 0x7f + +#define ADV7180_ADI_CTRL_REG 0x0e +#define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 + +#define ADV7180_STATUS1_REG 0x10 +#define ADV7180_STATUS1_IN_LOCK 0x01 +#define ADV7180_STATUS1_AUTOD_MASK 0x70 #define ADV7180_STATUS1_AUTOD_NTSM_M_J 0x00 #define ADV7180_STATUS1_AUTOD_NTSC_4_43 0x10 #define ADV7180_STATUS1_AUTOD_PAL_M 0x20 @@ -50,18 +73,37 @@ #define ADV7180_IDENT_REG 0x11 #define ADV7180_ID_7180 0x18 +#define ADV7180_ICONF1_ADI 0x40 +#define ADV7180_ICONF1_ACTIVE_LOW 0x01 +#define ADV7180_ICONF1_PSYNC_ONLY 0x10 +#define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 + +#define ADV7180_IRQ1_LOCK 0x01 +#define ADV7180_IRQ1_UNLOCK 0x02 +#define ADV7180_ISR1_ADI 0x42 +#define ADV7180_ICR1_ADI 0x43 +#define ADV7180_IMR1_ADI 0x44 +#define ADV7180_IMR2_ADI 0x48 +#define ADV7180_IRQ3_AD_CHANGE 0x08 +#define ADV7180_ISR3_ADI 0x4A +#define ADV7180_ICR3_ADI 0x4B +#define ADV7180_IMR3_ADI 0x4C +#define ADV7180_IMR4_ADI 0x50 struct adv7180_state { - struct v4l2_subdev sd; + struct v4l2_subdev sd; + struct work_struct work; + struct mutex mutex; /* mutual excl. when accessing chip */ + int irq; + v4l2_std_id curr_norm; + bool autodetect; }; -static v4l2_std_id determine_norm(struct i2c_client *client) +static v4l2_std_id adv7180_std_to_v4l2(u8 status1) { - u8 status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); - switch (status1 & ADV7180_STATUS1_AUTOD_MASK) { case ADV7180_STATUS1_AUTOD_NTSM_M_J: - return V4L2_STD_NTSC_M_JP; + return V4L2_STD_NTSC; case ADV7180_STATUS1_AUTOD_NTSC_4_43: return V4L2_STD_NTSC_443; case ADV7180_STATUS1_AUTOD_PAL_M: @@ -81,6 +123,53 @@ static v4l2_std_id determine_norm(struct i2c_client *client) } } +static int v4l2_std_to_adv7180(v4l2_std_id std) +{ + if (std == V4L2_STD_PAL_60) + return ADV7180_INPUT_CONTROL_PAL60; + if (std == V4L2_STD_NTSC_443) + return ADV7180_INPUT_CONTROL_NTSC_443; + if (std == V4L2_STD_PAL_N) + return ADV7180_INPUT_CONTROL_PAL_N; + if (std == V4L2_STD_PAL_M) + return ADV7180_INPUT_CONTROL_PAL_M; + if (std == V4L2_STD_PAL_Nc) + return ADV7180_INPUT_CONTROL_PAL_COMB_N; + + if (std & V4L2_STD_PAL) + return ADV7180_INPUT_CONTROL_PAL_BG; + if (std & V4L2_STD_NTSC) + return ADV7180_INPUT_CONTROL_NTSC_M; + if (std & V4L2_STD_SECAM) + return ADV7180_INPUT_CONTROL_PAL_SECAM; + + return -EINVAL; +} + +static u32 adv7180_status_to_v4l2(u8 status1) +{ + if (!(status1 & ADV7180_STATUS1_IN_LOCK)) + return V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int __adv7180_status(struct i2c_client *client, u32 *status, + v4l2_std_id *std) +{ + int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); + + if (status1 < 0) + return status1; + + if (status) + *status = adv7180_status_to_v4l2(status1); + if (std) + *std = adv7180_std_to_v4l2(status1); + + return 0; +} + static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) { return container_of(sd, struct adv7180_state, sd); @@ -88,10 +177,31 @@ static inline struct adv7180_state *to_state(struct v4l2_subdev *sd) static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) { - struct i2c_client *client = v4l2_get_subdevdata(sd); + struct adv7180_state *state = to_state(sd); + int err = mutex_lock_interruptible(&state->mutex); + if (err) + return err; + + /* when we are interrupt driven we know the state */ + if (!state->autodetect || state->irq > 0) + *std = state->curr_norm; + else + err = __adv7180_status(v4l2_get_subdevdata(sd), NULL, std); + + mutex_unlock(&state->mutex); + return err; +} - *std = determine_norm(client); - return 0; +static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + ret = __adv7180_status(v4l2_get_subdevdata(sd), status, NULL); + mutex_unlock(&state->mutex); + return ret; } static int adv7180_g_chip_ident(struct v4l2_subdev *sd, @@ -102,12 +212,51 @@ static int adv7180_g_chip_ident(struct v4l2_subdev *sd, return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7180, 0); } +static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) +{ + struct adv7180_state *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + /* all standards -> autodetect */ + if (std == V4L2_STD_ALL) { + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); + if (ret < 0) + goto out; + + __adv7180_status(client, NULL, &state->curr_norm); + state->autodetect = true; + } else { + ret = v4l2_std_to_adv7180(std); + if (ret < 0) + goto out; + + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, ret); + if (ret < 0) + goto out; + + state->curr_norm = std; + state->autodetect = false; + } + ret = 0; +out: + mutex_unlock(&state->mutex); + return ret; +} + static const struct v4l2_subdev_video_ops adv7180_video_ops = { .querystd = adv7180_querystd, + .g_input_status = adv7180_g_input_status, }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { .g_chip_ident = adv7180_g_chip_ident, + .s_std = adv7180_s_std, }; static const struct v4l2_subdev_ops adv7180_ops = { @@ -115,12 +264,45 @@ static const struct v4l2_subdev_ops adv7180_ops = { .video = &adv7180_video_ops, }; +static void adv7180_work(struct work_struct *work) +{ + struct adv7180_state *state = container_of(work, struct adv7180_state, + work); + struct i2c_client *client = v4l2_get_subdevdata(&state->sd); + u8 isr3; + + mutex_lock(&state->mutex); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + isr3 = i2c_smbus_read_byte_data(client, ADV7180_ISR3_ADI); + /* clear */ + i2c_smbus_write_byte_data(client, ADV7180_ICR3_ADI, isr3); + i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, 0); + + if (isr3 & ADV7180_IRQ3_AD_CHANGE && state->autodetect) + __adv7180_status(client, NULL, &state->curr_norm); + mutex_unlock(&state->mutex); + + enable_irq(state->irq); +} + +static irqreturn_t adv7180_irq(int irq, void *devid) +{ + struct adv7180_state *state = devid; + + schedule_work(&state->work); + + disable_irq_nosync(state->irq); + + return IRQ_HANDLED; +} + /* * Generic i2c probe * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' */ -static int adv7180_probe(struct i2c_client *client, +static __devinit int adv7180_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct adv7180_state *state; @@ -135,32 +317,111 @@ static int adv7180_probe(struct i2c_client *client, client->addr << 1, client->adapter->name); state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); - if (state == NULL) - return -ENOMEM; + if (state == NULL) { + ret = -ENOMEM; + goto err; + } + + state->irq = client->irq; + INIT_WORK(&state->work, adv7180_work); + mutex_init(&state->mutex); + state->autodetect = true; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); /* Initialize adv7180 */ - /* enable autodetection */ + /* Enable autodetection */ ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_PAL_BG_NTSC_J_SECAM); - if (ret > 0) - ret = i2c_smbus_write_byte_data(client, - ADV7180_AUTODETECT_ENABLE_REG, - ADV7180_AUTODETECT_DEFAULT); - if (ret < 0) { - printk(KERN_ERR DRIVER_NAME - ": Failed to communicate to chip: %d\n", ret); - return ret; + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) + goto err_unreg_subdev; + + /* ITU-R BT.656-4 compatible */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_EXTENDED_OUTPUT_CONTROL_REG, + ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS); + if (ret < 0) + goto err_unreg_subdev; + + /* read current norm */ + __adv7180_status(client, NULL, &state->curr_norm); + + /* register for interrupts */ + if (state->irq > 0) { + ret = request_irq(state->irq, adv7180_irq, 0, DRIVER_NAME, + state); + if (ret) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + if (ret < 0) + goto err_unreg_subdev; + + /* config the Interrupt pin to be active low */ + ret = i2c_smbus_write_byte_data(client, ADV7180_ICONF1_ADI, + ADV7180_ICONF1_ACTIVE_LOW | ADV7180_ICONF1_PSYNC_ONLY); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + /* enable AD change interrupts interrupts */ + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, + ADV7180_IRQ3_AD_CHANGE); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); + if (ret < 0) + goto err_unreg_subdev; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + 0); + if (ret < 0) + goto err_unreg_subdev; } return 0; + +err_unreg_subdev: + mutex_destroy(&state->mutex); + v4l2_device_unregister_subdev(sd); + kfree(state); +err: + printk(KERN_ERR DRIVER_NAME ": Failed to probe: %d\n", ret); + return ret; } -static int adv7180_remove(struct i2c_client *client) +static __devexit int adv7180_remove(struct i2c_client *client) { struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + + if (state->irq > 0) { + free_irq(client->irq, state); + if (cancel_work_sync(&state->work)) { + /* + * Work was pending, therefore we need to enable + * IRQ here to balance the disable_irq() done in the + * interrupt handler. + */ + enable_irq(state->irq); + } + } + mutex_destroy(&state->mutex); v4l2_device_unregister_subdev(sd); kfree(to_state(sd)); return 0; @@ -179,7 +440,7 @@ static struct i2c_driver adv7180_driver = { .name = DRIVER_NAME, }, .probe = adv7180_probe, - .remove = adv7180_remove, + .remove = __devexit_p(adv7180_remove), .id_table = adv7180_id, }; diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 51527d7b55a75..1485aee18d582 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -830,7 +830,7 @@ static int au0828_v4l2_close(struct file *filp) au0828_uninit_isoc(dev); /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* When close the device, set the usb intf0 into alt0 to free USB bandwidth */ diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c index f9330e3529c3e..5bb0f9e715837 100644 --- a/drivers/media/video/bt819.c +++ b/drivers/media/video/bt819.c @@ -299,7 +299,7 @@ static int bt819_s_routing(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "set input %x\n", input); - if (input < 0 || input > 7) + if (input > 7) return -EINVAL; if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL) diff --git a/drivers/media/video/bt8xx/bttv-input.c b/drivers/media/video/bt8xx/bttv-input.c index ebd51afe87619..84a957e52c4b4 100644 --- a/drivers/media/video/bt8xx/bttv-input.c +++ b/drivers/media/video/bt8xx/bttv-input.c @@ -73,12 +73,12 @@ static void ir_handle_key(struct bttv *btv) if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev,&ir->ir,data,data); + ir_input_keydown(ir->dev, &ir->ir, data); } else { /* HACK: Probably, ir->mask_keydown is missing for this board */ if (btv->c.type == BTTV_BOARD_WINFAST2000) - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); ir_input_nokey(ir->dev,&ir->ir); } @@ -104,7 +104,7 @@ static void ir_enltv_handle_key(struct bttv *btv) gpio, data, (gpio & ir->mask_keyup) ? " up" : "up/down"); - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); if (keyup) ir_input_nokey(ir->dev, &ir->ir); } else { @@ -118,7 +118,7 @@ static void ir_enltv_handle_key(struct bttv *btv) if (keyup) ir_input_nokey(ir->dev, &ir->ir); else - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); } ir->last_gpio = data | keyup; @@ -368,7 +368,10 @@ int bttv_input_init(struct bttv *btv) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(btv->c.pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -400,6 +403,7 @@ int bttv_input_init(struct bttv *btv) bttv_ir_stop(btv); btv->remote = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -411,6 +415,7 @@ void bttv_input_fini(struct bttv *btv) return; bttv_ir_stop(btv); + ir_input_free(btv->remote->dev); input_unregister_device(btv->remote->dev); kfree(btv->remote); btv->remote = NULL; diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c index 536dedb23ba36..4392c76af5df9 100644 --- a/drivers/media/video/cx18/cx18-av-core.c +++ b/drivers/media/video/cx18/cx18-av-core.c @@ -99,10 +99,8 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask, or_value); } -static int cx18_av_init(struct v4l2_subdev *sd, u32 val) +static void cx18_av_init(struct cx18 *cx) { - struct cx18 *cx = v4l2_get_subdevdata(sd); - /* * The crystal freq used in calculations in this driver will be * 28.636360 MHz. @@ -125,7 +123,6 @@ static int cx18_av_init(struct v4l2_subdev *sd, u32 val) /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56); - return 0; } static void cx18_av_initialize(struct v4l2_subdev *sd) @@ -198,7 +195,7 @@ static void cx18_av_initialize(struct v4l2_subdev *sd) cx18_av_and_or4(cx, CXADEC_CHIP_CTRL, 0xFFFBFFFF, 0x00120000); /* Setup the Video and and Aux/Audio PLLs */ - cx18_av_init(sd, 0); + cx18_av_init(cx); /* set video to auto-detect */ /* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */ @@ -1355,7 +1352,6 @@ static int cx18_av_s_register(struct v4l2_subdev *sd, static const struct v4l2_subdev_core_ops cx18_av_general_ops = { .g_chip_ident = cx18_av_g_chip_ident, .log_status = cx18_av_log_status, - .init = cx18_av_init, .load_fw = cx18_av_load_fw, .reset = cx18_av_reset, .queryctrl = cx18_av_queryctrl, @@ -1399,6 +1395,7 @@ int cx18_av_probe(struct cx18 *cx) { struct cx18_av_state *state = &cx->av_state; struct v4l2_subdev *sd; + int err; state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff; state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO) @@ -1417,5 +1414,8 @@ int cx18_av_probe(struct cx18 *cx) snprintf(sd->name, sizeof(sd->name), "%s %03x", cx->v4l2_dev.name, (state->rev >> 4)); sd->grp_id = CX18_HW_418_AV; - return v4l2_device_register_subdev(&cx->v4l2_dev, sd); + err = v4l2_device_register_subdev(&cx->v4l2_dev, sd); + if (!err) + cx18_av_init(cx); + return err; } diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h index 444e3c7c563e8..af3d71607dc98 100644 --- a/drivers/media/video/cx18/cx18-cards.h +++ b/drivers/media/video/cx18/cx18-cards.h @@ -34,6 +34,9 @@ #define CX18_HW_Z8F0811_IR_HAUP (CX18_HW_Z8F0811_IR_RX_HAUP | \ CX18_HW_Z8F0811_IR_TX_HAUP) +#define CX18_HW_IR_ANY (CX18_HW_Z8F0811_IR_RX_HAUP | \ + CX18_HW_Z8F0811_IR_TX_HAUP) + /* video inputs */ #define CX18_CARD_INPUT_VID_TUNER 1 #define CX18_CARD_INPUT_SVIDEO1 2 diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c index e12082b8a08df..7f65a47f12e1b 100644 --- a/drivers/media/video/cx18/cx18-driver.c +++ b/drivers/media/video/cx18/cx18-driver.c @@ -87,7 +87,6 @@ static int enc_ts_bufsize = CX18_DEFAULT_ENC_TS_BUFSIZE; static int enc_mpg_bufsize = CX18_DEFAULT_ENC_MPG_BUFSIZE; static int enc_idx_bufsize = CX18_DEFAULT_ENC_IDX_BUFSIZE; static int enc_yuv_bufsize = CX18_DEFAULT_ENC_YUV_BUFSIZE; -/* VBI bufsize based on standards supported by card tuner for now */ static int enc_pcm_bufsize = CX18_DEFAULT_ENC_PCM_BUFSIZE; static int enc_ts_bufs = -1; @@ -128,7 +127,6 @@ module_param(enc_ts_bufsize, int, 0644); module_param(enc_mpg_bufsize, int, 0644); module_param(enc_idx_bufsize, int, 0644); module_param(enc_yuv_bufsize, int, 0644); -/* VBI bufsize based on standards supported by card tuner for now */ module_param(enc_pcm_bufsize, int, 0644); module_param(enc_ts_bufs, int, 0644); @@ -211,7 +209,9 @@ MODULE_PARM_DESC(enc_yuv_buffers, "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS)); MODULE_PARM_DESC(enc_yuv_bufsize, "Size of an encoder YUV buffer (kB)\n" - "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFSIZE)); + "\t\t\tAllowed values are multiples of 33.75 kB rounded up\n" + "\t\t\t(multiples of size required for 32 screen lines)\n" + "\t\t\tDefault: 102"); MODULE_PARM_DESC(enc_yuv_bufs, "Number of encoder YUV buffers\n" "\t\t\tDefault is computed from other enc_yuv_* parameters"); @@ -220,7 +220,7 @@ MODULE_PARM_DESC(enc_vbi_buffers, "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS)); MODULE_PARM_DESC(enc_vbi_bufs, "Number of encoder VBI buffers\n" - "\t\t\tDefault is computed from enc_vbi_buffers & tuner std"); + "\t\t\tDefault is computed from enc_vbi_buffers"); MODULE_PARM_DESC(enc_pcm_buffers, "Encoder PCM buffer memory (MB). (enc_pcm_bufs can override)\n" "\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS)); @@ -499,10 +499,27 @@ static void cx18_process_options(struct cx18 *cx) continue; } /* + * YUV is a special case where the stream_buf_size needs to be + * an integral multiple of 33.75 kB (storage for 32 screens + * lines to maintain alignment in case of lost buffers + */ + if (i == CX18_ENC_STREAM_TYPE_YUV) { + cx->stream_buf_size[i] *= 1024; + cx->stream_buf_size[i] -= + (cx->stream_buf_size[i] % CX18_UNIT_ENC_YUV_BUFSIZE); + + if (cx->stream_buf_size[i] < CX18_UNIT_ENC_YUV_BUFSIZE) + cx->stream_buf_size[i] = + CX18_UNIT_ENC_YUV_BUFSIZE; + } + /* + * YUV is a special case where the stream_buf_size is + * now in bytes. * VBI is a special case where the stream_buf_size is fixed * and already in bytes */ - if (i == CX18_ENC_STREAM_TYPE_VBI) { + if (i == CX18_ENC_STREAM_TYPE_VBI || + i == CX18_ENC_STREAM_TYPE_YUV) { if (cx->stream_buffers[i] < 0) { cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 * 1024 @@ -513,18 +530,24 @@ static void cx18_process_options(struct cx18 *cx) cx->stream_buffers[i] * cx->stream_buf_size[i]/(1024 * 1024); } - continue; - } - /* All other streams have stream_buf_size in kB at this point */ - if (cx->stream_buffers[i] < 0) { - cx->stream_buffers[i] = cx->options.megabytes[i] * 1024 - / cx->stream_buf_size[i]; } else { - /* N.B. This might round down to 0 */ - cx->options.megabytes[i] = - cx->stream_buffers[i] * cx->stream_buf_size[i] / 1024; + /* All other streams have stream_buf_size in kB here */ + if (cx->stream_buffers[i] < 0) { + cx->stream_buffers[i] = + cx->options.megabytes[i] * 1024 + / cx->stream_buf_size[i]; + } else { + /* N.B. This might round down to 0 */ + cx->options.megabytes[i] = + cx->stream_buffers[i] + * cx->stream_buf_size[i] / 1024; + } + /* convert from kB to bytes */ + cx->stream_buf_size[i] *= 1024; } - cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */ + CX18_DEBUG_INFO("Stream type %d options: %d MB, %d buffers, " + "%d bytes\n", i, cx->options.megabytes[i], + cx->stream_buffers[i], cx->stream_buf_size[i]); } cx->options.cardtype = cardtype[cx->instance]; @@ -669,6 +692,12 @@ static int __devinit cx18_init_struct1(struct cx18 *cx) cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE; cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced; + /* IVTV style VBI insertion into MPEG streams */ + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_buf.list); + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.list); + INIT_LIST_HEAD(&cx->vbi.sliced_mpeg_mdl.buf_list); + list_add(&cx->vbi.sliced_mpeg_buf.list, + &cx->vbi.sliced_mpeg_mdl.buf_list); return 0; } @@ -883,7 +912,6 @@ static int __devinit cx18_probe(struct pci_dev *pci_dev, CX18_ERR("Could not register A/V decoder subdevice\n"); goto free_map; } - cx18_call_hw(cx, CX18_HW_418_AV, core, init, 0); /* Initialize GPIO Reset Controller to do chip resets during i2c init */ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) { diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h index c6a1e907f63af..e3f7911a7385b 100644 --- a/drivers/media/video/cx18/cx18-driver.h +++ b/drivers/media/video/cx18/cx18-driver.h @@ -50,6 +50,7 @@ #include #include #include +#include #include "cx18-mailbox.h" #include "cx18-av-core.h" #include "cx23418.h" @@ -120,12 +121,16 @@ /* Maximum firmware DMA buffers per stream */ #define CX18_MAX_FW_MDLS_PER_STREAM 63 +/* YUV buffer sizes in bytes to ensure integer # of frames per buffer */ +#define CX18_UNIT_ENC_YUV_BUFSIZE (720 * 32 * 3 / 2) /* bytes */ +#define CX18_625_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 576/32) +#define CX18_525_LINE_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 480/32) + /* DMA buffer, default size in kB allocated */ #define CX18_DEFAULT_ENC_TS_BUFSIZE 32 #define CX18_DEFAULT_ENC_MPG_BUFSIZE 32 #define CX18_DEFAULT_ENC_IDX_BUFSIZE 32 -#define CX18_DEFAULT_ENC_YUV_BUFSIZE 128 -/* Default VBI bufsize based on standards supported by card tuner for now */ +#define CX18_DEFAULT_ENC_YUV_BUFSIZE (CX18_UNIT_ENC_YUV_BUFSIZE * 3 / 1024 + 1) #define CX18_DEFAULT_ENC_PCM_BUFSIZE 4 /* i2c stuff */ @@ -246,8 +251,8 @@ struct cx18_options { int radio; /* enable/disable radio */ }; -/* per-buffer bit flags */ -#define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */ +/* per-mdl bit flags */ +#define CX18_F_M_NEED_SWAP 0 /* mdl buffer data must be endianess swapped */ /* per-stream, s_flags */ #define CX18_F_S_CLAIMED 3 /* this stream is claimed */ @@ -274,18 +279,29 @@ struct cx18_options { struct cx18_buffer { struct list_head list; dma_addr_t dma_handle; - u32 id; - unsigned long b_flags; - unsigned skipped; char *buf; u32 bytesused; u32 readpos; }; +struct cx18_mdl { + struct list_head list; + u32 id; /* index into cx->scb->cpu_mdl[] of 1st cx18_mdl_ent */ + + unsigned int skipped; + unsigned long m_flags; + + struct list_head buf_list; + struct cx18_buffer *curr_buf; /* current buffer in list for reading */ + + u32 bytesused; + u32 readpos; +}; + struct cx18_queue { struct list_head list; - atomic_t buffers; + atomic_t depth; u32 bytesused; spinlock_t lock; }; @@ -337,7 +353,7 @@ struct cx18_stream { const char *name; /* name of the stream */ int type; /* stream type */ u32 handle; /* task handle */ - unsigned mdl_offset; + unsigned int mdl_base_idx; u32 id; unsigned long s_flags; /* status flags, see above */ @@ -346,14 +362,20 @@ struct cx18_stream { PCI_DMA_NONE */ wait_queue_head_t waitq; - /* Buffer Stats */ - u32 buffers; - u32 buf_size; + /* Buffers */ + struct list_head buf_pool; /* buffers not attached to an MDL */ + u32 buffers; /* total buffers owned by this stream */ + u32 buf_size; /* size in bytes of a single buffer */ + + /* MDL sizes - all stream MDLs are the same size */ + u32 bufs_per_mdl; + u32 mdl_size; /* total bytes in all buffers in a mdl */ - /* Buffer Queues */ - struct cx18_queue q_free; /* free buffers */ - struct cx18_queue q_busy; /* busy buffers - in use by firmware */ - struct cx18_queue q_full; /* full buffers - data for user apps */ + /* MDL Queues */ + struct cx18_queue q_free; /* free - in rotation, not committed */ + struct cx18_queue q_busy; /* busy - in use by firmware */ + struct cx18_queue q_full; /* full - data for user apps */ + struct cx18_queue q_idle; /* idle - not in rotation */ struct work_struct out_work_order; @@ -481,10 +503,11 @@ struct vbi_info { u32 inserted_frame; /* - * A dummy driver stream transfer buffer with a copy of the next + * A dummy driver stream transfer mdl & buffer with a copy of the next * sliced_mpeg_data[] buffer for output to userland apps. * Only used in cx18-fileops.c, but its state needs to persist at times. */ + struct cx18_mdl sliced_mpeg_mdl; struct cx18_buffer sliced_mpeg_buf; }; @@ -511,10 +534,9 @@ struct cx18 { u8 is_60hz; u8 nof_inputs; /* number of video inputs */ u8 nof_audio_inputs; /* number of audio inputs */ - u16 buffer_id; /* buffer ID counter */ u32 v4l2_cap; /* V4L2 capabilities of card */ u32 hw_flags; /* Hardware description of the board */ - unsigned mdl_offset; + unsigned int free_mdl_idx; struct cx18_scb __iomem *scb; /* pointer to SCB */ struct mutex epu2apu_mb_lock; /* protect driver to chip mailbox in SCB*/ struct mutex epu2cpu_mb_lock; /* protect driver to chip mailbox in SCB*/ @@ -585,6 +607,8 @@ struct cx18 { struct i2c_algo_bit_data i2c_algo[2]; struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2]; + struct IR_i2c_init_data ir_i2c_init_data; + /* gpio */ u32 gpio_dir; u32 gpio_val; diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c index 51a0c33b25b7f..71ad2d1b4c2c2 100644 --- a/drivers/media/video/cx18/cx18-dvb.c +++ b/drivers/media/video/cx18/cx18-dvb.c @@ -61,6 +61,7 @@ static struct mxl5005s_config hauppauge_hvr1600_tuner = { .top = MXL5005S_TOP_25P2, .mod_mode = MXL_DIGITAL_MODE, .if_mode = MXL_ZERO_IF, + .qam_gain = 0x02, .AgcMasterByte = 0x00, }; @@ -71,7 +72,8 @@ static struct s5h1409_config hauppauge_hvr1600_config = { .qam_if = 44000, .inversion = S5H1409_INVERSION_OFF, .status_mode = S5H1409_DEMODLOCKING, - .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK + .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK, + .hvr1600_opt = S5H1409_HVR1600_OPTIMIZE }; /* @@ -360,9 +362,10 @@ int cx18_dvb_register(struct cx18_stream *stream) dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx); CX18_INFO("DVB Frontend registered\n"); - CX18_INFO("Registered DVB adapter%d for %s (%d x %d kB)\n", + CX18_INFO("Registered DVB adapter%d for %s (%d x %d.%02d kB)\n", stream->dvb.dvb_adapter.num, stream->name, - stream->buffers, stream->buf_size/1024); + stream->buffers, stream->buf_size/1024, + (stream->buf_size * 100 / 1024) % 100); mutex_init(&dvb->feedlock); dvb->enabled = 1; diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c index 04d9c2508b866..4e278db31cc9a 100644 --- a/drivers/media/video/cx18/cx18-fileops.c +++ b/drivers/media/video/cx18/cx18-fileops.c @@ -166,11 +166,12 @@ static void cx18_dualwatch(struct cx18 *cx) } -static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err) +static struct cx18_mdl *cx18_get_mdl(struct cx18_stream *s, int non_block, + int *err) { struct cx18 *cx = s->cx; struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI]; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; DEFINE_WAIT(wait); *err = 0; @@ -185,32 +186,33 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, } if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) && !test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) { - while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) { + while ((mdl = cx18_dequeue(s_vbi, + &s_vbi->q_full))) { /* byteswap and process VBI data */ - cx18_process_vbi_data(cx, buf, + cx18_process_vbi_data(cx, mdl, s_vbi->type); - cx18_stream_put_buf_fw(s_vbi, buf); + cx18_stream_put_mdl_fw(s_vbi, mdl); } } - buf = &cx->vbi.sliced_mpeg_buf; - if (buf->readpos != buf->bytesused) - return buf; + mdl = &cx->vbi.sliced_mpeg_mdl; + if (mdl->readpos != mdl->bytesused) + return mdl; } /* do we have new data? */ - buf = cx18_dequeue(s, &s->q_full); - if (buf) { - if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP, - &buf->b_flags)) - return buf; + mdl = cx18_dequeue(s, &s->q_full); + if (mdl) { + if (!test_and_clear_bit(CX18_F_M_NEED_SWAP, + &mdl->m_flags)) + return mdl; if (s->type == CX18_ENC_STREAM_TYPE_MPG) /* byteswap MPG data */ - cx18_buf_swap(buf); + cx18_mdl_swap(mdl); else { /* byteswap and process VBI data */ - cx18_process_vbi_data(cx, buf, s->type); + cx18_process_vbi_data(cx, mdl, s->type); } - return buf; + return mdl; } /* return if end of stream */ @@ -229,7 +231,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE); /* New buffers might have become available before we were added to the waitqueue */ - if (!atomic_read(&s->q_full.buffers)) + if (!atomic_read(&s->q_full.depth)) schedule(); finish_wait(&s->waitq, &wait); if (signal_pending(current)) { @@ -241,21 +243,28 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, } } -static void cx18_setup_sliced_vbi_buf(struct cx18 *cx) +static void cx18_setup_sliced_vbi_mdl(struct cx18 *cx) { + struct cx18_mdl *mdl = &cx->vbi.sliced_mpeg_mdl; + struct cx18_buffer *buf = &cx->vbi.sliced_mpeg_buf; int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; - cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx]; - cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx]; - cx->vbi.sliced_mpeg_buf.readpos = 0; + buf->buf = cx->vbi.sliced_mpeg_data[idx]; + buf->bytesused = cx->vbi.sliced_mpeg_size[idx]; + buf->readpos = 0; + + mdl->curr_buf = NULL; + mdl->bytesused = cx->vbi.sliced_mpeg_size[idx]; + mdl->readpos = 0; } static size_t cx18_copy_buf_to_user(struct cx18_stream *s, - struct cx18_buffer *buf, char __user *ubuf, size_t ucount) + struct cx18_buffer *buf, char __user *ubuf, size_t ucount, bool *stop) { struct cx18 *cx = s->cx; size_t len = buf->bytesused - buf->readpos; + *stop = false; if (len > ucount) len = ucount; if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG && @@ -335,7 +344,8 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, /* We declare we actually found a Program Pack*/ cx->search_pack_header = 0; /* expect vid PES */ len = (char *)q - start; - cx18_setup_sliced_vbi_buf(cx); + cx18_setup_sliced_vbi_mdl(cx); + *stop = true; break; } } @@ -352,6 +362,60 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s, return len; } +/** + * list_entry_is_past_end - check if a previous loop cursor is off list end + * @pos: the type * previously used as a loop cursor. + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + * + * Check if the entry's list_head is the head of the list, thus it's not a + * real entry but was the loop cursor that walked past the end + */ +#define list_entry_is_past_end(pos, head, member) \ + (&pos->member == (head)) + +static size_t cx18_copy_mdl_to_user(struct cx18_stream *s, + struct cx18_mdl *mdl, char __user *ubuf, size_t ucount) +{ + size_t tot_written = 0; + int rc; + bool stop = false; + + if (mdl->curr_buf == NULL) + mdl->curr_buf = list_first_entry(&mdl->buf_list, + struct cx18_buffer, list); + + if (list_entry_is_past_end(mdl->curr_buf, &mdl->buf_list, list)) { + /* + * For some reason we've exhausted the buffers, but the MDL + * object still said some data was unread. + * Fix that and bail out. + */ + mdl->readpos = mdl->bytesused; + return 0; + } + + list_for_each_entry_from(mdl->curr_buf, &mdl->buf_list, list) { + + if (mdl->curr_buf->readpos >= mdl->curr_buf->bytesused) + continue; + + rc = cx18_copy_buf_to_user(s, mdl->curr_buf, ubuf + tot_written, + ucount - tot_written, &stop); + if (rc < 0) + return rc; + mdl->readpos += rc; + tot_written += rc; + + if (stop || /* Forced stopping point for VBI insertion */ + tot_written >= ucount || /* Reader request statisfied */ + mdl->curr_buf->readpos < mdl->curr_buf->bytesused || + mdl->readpos >= mdl->bytesused) /* MDL buffers drained */ + break; + } + return tot_written; +} + static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, size_t tot_count, int non_block) { @@ -373,12 +437,12 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, single_frame = 1; for (;;) { - struct cx18_buffer *buf; + struct cx18_mdl *mdl; int rc; - buf = cx18_get_buffer(s, non_block, &rc); + mdl = cx18_get_mdl(s, non_block, &rc); /* if there is no data available... */ - if (buf == NULL) { + if (mdl == NULL) { /* if we got data, then return that regardless */ if (tot_written) break; @@ -392,20 +456,20 @@ static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf, return rc; } - rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written, + rc = cx18_copy_mdl_to_user(s, mdl, ubuf + tot_written, tot_count - tot_written); - if (buf != &cx->vbi.sliced_mpeg_buf) { - if (buf->readpos == buf->bytesused) - cx18_stream_put_buf_fw(s, buf); + if (mdl != &cx->vbi.sliced_mpeg_mdl) { + if (mdl->readpos == mdl->bytesused) + cx18_stream_put_mdl_fw(s, mdl); else - cx18_push(s, buf, &s->q_full); - } else if (buf->readpos == buf->bytesused) { + cx18_push(s, mdl, &s->q_full); + } else if (mdl->readpos == mdl->bytesused) { int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES; cx->vbi.sliced_mpeg_size[idx] = 0; cx->vbi.inserted_frame++; - cx->vbi_data_inserted += buf->bytesused; + cx->vbi_data_inserted += mdl->bytesused; } if (rc < 0) return rc; @@ -543,7 +607,7 @@ unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait) CX18_DEBUG_HI_FILE("Encoder poll\n"); poll_wait(filp, &s->waitq, wait); - if (atomic_read(&s->q_full.buffers)) + if (atomic_read(&s->q_full.depth)) return POLLIN | POLLRDNORM; if (eof) return POLLHUP; diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c index 2477461e84d7c..eecf29af916c2 100644 --- a/drivers/media/video/cx18/cx18-i2c.c +++ b/drivers/media/video/cx18/cx18-i2c.c @@ -28,7 +28,6 @@ #include "cx18-gpio.h" #include "cx18-i2c.h" #include "cx18-irq.h" -#include #define CX18_REG_I2C_1_WR 0xf15000 #define CX18_REG_I2C_1_RD 0xf15008 @@ -97,17 +96,11 @@ static const char * const hw_devicenames[] = { "ir_rx_z8f0811_haup", }; -static const struct IR_i2c_init_data z8f0811_ir_init_data = { - .ir_codes = &ir_codes_hauppauge_new_table, - .internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR, - .type = IR_TYPE_RC5, - .name = "CX23418 Z8F0811 Hauppauge", -}; - -static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, - u8 addr) +static int cx18_i2c_new_ir(struct cx18 *cx, struct i2c_adapter *adap, u32 hw, + const char *type, u8 addr) { struct i2c_board_info info; + struct IR_i2c_init_data *init_data = &cx->ir_i2c_init_data; unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; memset(&info, 0, sizeof(struct i2c_board_info)); @@ -116,9 +109,11 @@ static int cx18_i2c_new_ir(struct i2c_adapter *adap, u32 hw, const char *type, /* Our default information for ir-kbd-i2c.c to use */ switch (hw) { case CX18_HW_Z8F0811_IR_RX_HAUP: - info.platform_data = (void *) &z8f0811_ir_init_data; - break; - default: + init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = IR_TYPE_RC5; + init_data->name = cx->card_name; + info.platform_data = init_data; break; } @@ -154,8 +149,8 @@ int cx18_i2c_register(struct cx18 *cx, unsigned idx) return sd != NULL ? 0 : -1; } - if (hw & CX18_HW_Z8F0811_IR_HAUP) - return cx18_i2c_new_ir(adap, hw, type, hw_addrs[idx]); + if (hw & CX18_HW_IR_ANY) + return cx18_i2c_new_ir(cx, adap, hw, type, hw_addrs[idx]); /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index fc76e4d6ffa72..3e4fc192fdec4 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -910,7 +910,8 @@ static int cx18_log_status(struct file *file, void *fh) continue; CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags, - atomic_read(&s->q_full.buffers) * 100 / s->buffers, + atomic_read(&s->q_full.depth) * s->bufs_per_mdl * 100 + / s->buffers, (s->buffers * s->buf_size) / 1024, s->buffers); } CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n", diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index afe46c3d4057a..f231dd09c720a 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -131,13 +131,39 @@ static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name) * Functions that run in a work_queue work handling context */ +static void cx18_mdl_send_to_dvb(struct cx18_stream *s, struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + if (!s->dvb.enabled || mdl->bytesused == 0) + return; + + /* We ignore mdl and buf readpos accounting here - it doesn't matter */ + + /* The likely case */ + if (list_is_singular(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, + list); + if (buf->bytesused) + dvb_dmx_swfilter(&s->dvb.demux, + buf->buf, buf->bytesused); + return; + } + + list_for_each_entry(buf, &mdl->buf_list, list) { + if (buf->bytesused == 0) + break; + dvb_dmx_swfilter(&s->dvb.demux, buf->buf, buf->bytesused); + } +} + static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) { u32 handle, mdl_ack_count, id; struct cx18_mailbox *mb; struct cx18_mdl_ack *mdl_ack; struct cx18_stream *s; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; int i; mb = &order->mb; @@ -158,7 +184,7 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) id = mdl_ack->id; /* * Simple integrity check for processing a stale (and possibly - * inconsistent mailbox): make sure the buffer id is in the + * inconsistent mailbox): make sure the MDL id is in the * valid range for the stream. * * We go through the trouble of dealing with stale mailboxes @@ -169,44 +195,42 @@ static void epu_dma_done(struct cx18 *cx, struct cx18_in_work_order *order) * There are occasions when we get a half changed mailbox, * which this check catches for a handle & id mismatch. If the * handle and id do correspond, the worst case is that we - * completely lost the old buffer, but pick up the new buffer + * completely lost the old MDL, but pick up the new MDL * early (but the new mdl_ack is guaranteed to be good in this * case as the firmware wouldn't point us to a new mdl_ack until * it's filled in). * - * cx18_queue_get buf() will detect the lost buffers + * cx18_queue_get_mdl() will detect the lost MDLs * and send them back to q_free for fw rotation eventually. */ if ((order->flags & CX18_F_EWO_MB_STALE_UPON_RECEIPT) && - !(id >= s->mdl_offset && - id < (s->mdl_offset + s->buffers))) { + !(id >= s->mdl_base_idx && + id < (s->mdl_base_idx + s->buffers))) { CX18_WARN("Fell behind! Ignoring stale mailbox with " - " inconsistent data. Lost buffer for mailbox " + " inconsistent data. Lost MDL for mailbox " "seq no %d\n", mb->request); break; } - buf = cx18_queue_get_buf(s, id, mdl_ack->data_used); + mdl = cx18_queue_get_mdl(s, id, mdl_ack->data_used); - CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id); - if (buf == NULL) { - CX18_WARN("Could not find buf %d for stream %s\n", + CX18_DEBUG_HI_DMA("DMA DONE for %s (MDL %d)\n", s->name, id); + if (mdl == NULL) { + CX18_WARN("Could not find MDL %d for stream %s\n", id, s->name); continue; } CX18_DEBUG_HI_DMA("%s recv bytesused = %d\n", - s->name, buf->bytesused); + s->name, mdl->bytesused); if (s->type != CX18_ENC_STREAM_TYPE_TS) - cx18_enqueue(s, buf, &s->q_full); + cx18_enqueue(s, mdl, &s->q_full); else { - if (s->dvb.enabled) - dvb_dmx_swfilter(&s->dvb.demux, buf->buf, - buf->bytesused); - cx18_enqueue(s, buf, &s->q_free); + cx18_mdl_send_to_dvb(s, mdl); + cx18_enqueue(s, mdl, &s->q_free); } } - /* Put as many buffers as possible back into fw use */ + /* Put as many MDLs as possible back into fw use */ cx18_stream_load_fw_queue(s); wake_up(&cx->dma_waitq); @@ -616,7 +640,7 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) /* * Wait for XPU to perform extra actions for the caller in some cases. - * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all buffers + * e.g. CX18_CPU_DE_RELEASE_MDL will cause the CPU to send all MDLs * back in a burst shortly thereafter */ if (info->flags & API_SLOW) diff --git a/drivers/media/video/cx18/cx18-mailbox.h b/drivers/media/video/cx18/cx18-mailbox.h index 522ad534034cb..33a3491c45374 100644 --- a/drivers/media/video/cx18/cx18-mailbox.h +++ b/drivers/media/video/cx18/cx18-mailbox.h @@ -39,14 +39,14 @@ struct cx18; /* - * This structure is used by CPU to provide completed buffers information - * Its structure is dictrated by the layout of the SCB, required by the + * This structure is used by CPU to provide completed MDL & buffers information. + * Its structure is dictated by the layout of the SCB, required by the * firmware, but its definition needs to be here, instead of in cx18-scb.h, * for mailbox work order scheduling */ struct cx18_mdl_ack { u32 id; /* ID of a completed MDL */ - u32 data_used; /* Total data filled in the MDL for buffer 'id' */ + u32 data_used; /* Total data filled in the MDL with 'id' */ }; /* The cx18_mailbox struct is the mailbox structure which is used for passing diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c index fa1ed7897d97a..63304823cef52 100644 --- a/drivers/media/video/cx18/cx18-queue.c +++ b/drivers/media/video/cx18/cx18-queue.c @@ -26,6 +26,7 @@ #include "cx18-queue.h" #include "cx18-streams.h" #include "cx18-scb.h" +#include "cx18-io.h" void cx18_buf_swap(struct cx18_buffer *buf) { @@ -35,151 +36,312 @@ void cx18_buf_swap(struct cx18_buffer *buf) swab32s((u32 *)(buf->buf + i)); } +void _cx18_mdl_swap(struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + list_for_each_entry(buf, &mdl->buf_list, list) { + if (buf->bytesused == 0) + break; + cx18_buf_swap(buf); + } +} + void cx18_queue_init(struct cx18_queue *q) { INIT_LIST_HEAD(&q->list); - atomic_set(&q->buffers, 0); + atomic_set(&q->depth, 0); q->bytesused = 0; } -struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q, int to_front) { - /* clear the buffer if it is not to be enqueued to the full queue */ + /* clear the mdl if it is not to be enqueued to the full queue */ if (q != &s->q_full) { - buf->bytesused = 0; - buf->readpos = 0; - buf->b_flags = 0; - buf->skipped = 0; + mdl->bytesused = 0; + mdl->readpos = 0; + mdl->m_flags = 0; + mdl->skipped = 0; + mdl->curr_buf = NULL; } /* q_busy is restricted to a max buffer count imposed by firmware */ if (q == &s->q_busy && - atomic_read(&q->buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) + atomic_read(&q->depth) >= CX18_MAX_FW_MDLS_PER_STREAM) q = &s->q_free; spin_lock(&q->lock); if (to_front) - list_add(&buf->list, &q->list); /* LIFO */ + list_add(&mdl->list, &q->list); /* LIFO */ else - list_add_tail(&buf->list, &q->list); /* FIFO */ - q->bytesused += buf->bytesused - buf->readpos; - atomic_inc(&q->buffers); + list_add_tail(&mdl->list, &q->list); /* FIFO */ + q->bytesused += mdl->bytesused - mdl->readpos; + atomic_inc(&q->depth); spin_unlock(&q->lock); return q; } -struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) +struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q) { - struct cx18_buffer *buf = NULL; + struct cx18_mdl *mdl = NULL; spin_lock(&q->lock); if (!list_empty(&q->list)) { - buf = list_first_entry(&q->list, struct cx18_buffer, list); - list_del_init(&buf->list); - q->bytesused -= buf->bytesused - buf->readpos; - buf->skipped = 0; - atomic_dec(&q->buffers); + mdl = list_first_entry(&q->list, struct cx18_mdl, list); + list_del_init(&mdl->list); + q->bytesused -= mdl->bytesused - mdl->readpos; + mdl->skipped = 0; + atomic_dec(&q->depth); } spin_unlock(&q->lock); - return buf; + return mdl; +} + +static void _cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + u32 buf_size = s->buf_size; + u32 bytesused = mdl->bytesused; + + list_for_each_entry(buf, &mdl->buf_list, list) { + buf->readpos = 0; + if (bytesused >= buf_size) { + buf->bytesused = buf_size; + bytesused -= buf_size; + } else { + buf->bytesused = bytesused; + bytesused = 0; + } + cx18_buf_sync_for_cpu(s, buf); + } +} + +static inline void cx18_mdl_update_bufs_for_cpu(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + struct cx18_buffer *buf; + + if (list_is_singular(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, struct cx18_buffer, + list); + buf->bytesused = mdl->bytesused; + buf->readpos = 0; + cx18_buf_sync_for_cpu(s, buf); + } else { + _cx18_mdl_update_bufs_for_cpu(s, mdl); + } } -struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, +struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, u32 bytesused) { struct cx18 *cx = s->cx; - struct cx18_buffer *buf; - struct cx18_buffer *tmp; - struct cx18_buffer *ret = NULL; + struct cx18_mdl *mdl; + struct cx18_mdl *tmp; + struct cx18_mdl *ret = NULL; LIST_HEAD(sweep_up); /* * We don't have to acquire multiple q locks here, because we are * serialized by the single threaded work handler. - * Buffers from the firmware will thus remain in order as + * MDLs from the firmware will thus remain in order as * they are moved from q_busy to q_full or to the dvb ring buffer. */ spin_lock(&s->q_busy.lock); - list_for_each_entry_safe(buf, tmp, &s->q_busy.list, list) { + list_for_each_entry_safe(mdl, tmp, &s->q_busy.list, list) { /* * We should find what the firmware told us is done, * right at the front of the queue. If we don't, we likely have - * missed a buffer done message from the firmware. - * Once we skip a buffer repeatedly, relative to the size of + * missed an mdl done message from the firmware. + * Once we skip an mdl repeatedly, relative to the size of * q_busy, we have high confidence we've missed it. */ - if (buf->id != id) { - buf->skipped++; - if (buf->skipped >= atomic_read(&s->q_busy.buffers)-1) { - /* buffer must have fallen out of rotation */ - CX18_WARN("Skipped %s, buffer %d, %d " + if (mdl->id != id) { + mdl->skipped++; + if (mdl->skipped >= atomic_read(&s->q_busy.depth)-1) { + /* mdl must have fallen out of rotation */ + CX18_WARN("Skipped %s, MDL %d, %d " "times - it must have dropped out of " - "rotation\n", s->name, buf->id, - buf->skipped); + "rotation\n", s->name, mdl->id, + mdl->skipped); /* Sweep it up to put it back into rotation */ - list_move_tail(&buf->list, &sweep_up); - atomic_dec(&s->q_busy.buffers); + list_move_tail(&mdl->list, &sweep_up); + atomic_dec(&s->q_busy.depth); } continue; } /* - * We pull the desired buffer off of the queue here. Something + * We pull the desired mdl off of the queue here. Something * will have to put it back on a queue later. */ - list_del_init(&buf->list); - atomic_dec(&s->q_busy.buffers); - ret = buf; + list_del_init(&mdl->list); + atomic_dec(&s->q_busy.depth); + ret = mdl; break; } spin_unlock(&s->q_busy.lock); /* - * We found the buffer for which we were looking. Get it ready for + * We found the mdl for which we were looking. Get it ready for * the caller to put on q_full or in the dvb ring buffer. */ if (ret != NULL) { ret->bytesused = bytesused; ret->skipped = 0; - /* readpos and b_flags were 0'ed when the buf went on q_busy */ - cx18_buf_sync_for_cpu(s, ret); + /* 0'ed readpos, m_flags & curr_buf when mdl went on q_busy */ + cx18_mdl_update_bufs_for_cpu(s, ret); if (s->type != CX18_ENC_STREAM_TYPE_TS) - set_bit(CX18_F_B_NEED_BUF_SWAP, &ret->b_flags); + set_bit(CX18_F_M_NEED_SWAP, &ret->m_flags); } - /* Put any buffers the firmware is ignoring back into normal rotation */ - list_for_each_entry_safe(buf, tmp, &sweep_up, list) { - list_del_init(&buf->list); - cx18_enqueue(s, buf, &s->q_free); + /* Put any mdls the firmware is ignoring back into normal rotation */ + list_for_each_entry_safe(mdl, tmp, &sweep_up, list) { + list_del_init(&mdl->list); + cx18_enqueue(s, mdl, &s->q_free); } return ret; } -/* Move all buffers of a queue to q_free, while flushing the buffers */ -static void cx18_queue_flush(struct cx18_stream *s, struct cx18_queue *q) +/* Move all mdls of a queue, while flushing the mdl */ +static void cx18_queue_flush(struct cx18_stream *s, + struct cx18_queue *q_src, struct cx18_queue *q_dst) { - struct cx18_buffer *buf; + struct cx18_mdl *mdl; - if (q == &s->q_free) + /* It only makes sense to flush to q_free or q_idle */ + if (q_src == q_dst || q_dst == &s->q_full || q_dst == &s->q_busy) return; - spin_lock(&q->lock); - while (!list_empty(&q->list)) { - buf = list_first_entry(&q->list, struct cx18_buffer, list); - list_move_tail(&buf->list, &s->q_free.list); - buf->bytesused = buf->readpos = buf->b_flags = buf->skipped = 0; - atomic_inc(&s->q_free.buffers); + spin_lock(&q_src->lock); + spin_lock(&q_dst->lock); + while (!list_empty(&q_src->list)) { + mdl = list_first_entry(&q_src->list, struct cx18_mdl, list); + list_move_tail(&mdl->list, &q_dst->list); + mdl->bytesused = 0; + mdl->readpos = 0; + mdl->m_flags = 0; + mdl->skipped = 0; + mdl->curr_buf = NULL; + atomic_inc(&q_dst->depth); } - cx18_queue_init(q); - spin_unlock(&q->lock); + cx18_queue_init(q_src); + spin_unlock(&q_src->lock); + spin_unlock(&q_dst->lock); } void cx18_flush_queues(struct cx18_stream *s) { - cx18_queue_flush(s, &s->q_busy); - cx18_queue_flush(s, &s->q_full); + cx18_queue_flush(s, &s->q_busy, &s->q_free); + cx18_queue_flush(s, &s->q_full, &s->q_free); +} + +/* + * Note, s->buf_pool is not protected by a lock, + * the stream better not have *anything* going on when calling this + */ +void cx18_unload_queues(struct cx18_stream *s) +{ + struct cx18_queue *q_idle = &s->q_idle; + struct cx18_mdl *mdl; + struct cx18_buffer *buf; + + /* Move all MDLS to q_idle */ + cx18_queue_flush(s, &s->q_busy, q_idle); + cx18_queue_flush(s, &s->q_full, q_idle); + cx18_queue_flush(s, &s->q_free, q_idle); + + /* Reset MDL id's and move all buffers back to the stream's buf_pool */ + spin_lock(&q_idle->lock); + list_for_each_entry(mdl, &q_idle->list, list) { + while (!list_empty(&mdl->buf_list)) { + buf = list_first_entry(&mdl->buf_list, + struct cx18_buffer, list); + list_move_tail(&buf->list, &s->buf_pool); + buf->bytesused = 0; + buf->readpos = 0; + } + mdl->id = s->mdl_base_idx; /* reset id to a "safe" value */ + /* all other mdl fields were cleared by cx18_queue_flush() */ + } + spin_unlock(&q_idle->lock); +} + +/* + * Note, s->buf_pool is not protected by a lock, + * the stream better not have *anything* going on when calling this + */ +void cx18_load_queues(struct cx18_stream *s) +{ + struct cx18 *cx = s->cx; + struct cx18_mdl *mdl; + struct cx18_buffer *buf; + int mdl_id; + int i; + u32 partial_buf_size; + + /* + * Attach buffers to MDLs, give the MDLs ids, and add MDLs to q_free + * Excess MDLs are left on q_idle + * Excess buffers are left in buf_pool and/or on an MDL in q_idle + */ + mdl_id = s->mdl_base_idx; + for (mdl = cx18_dequeue(s, &s->q_idle), i = s->bufs_per_mdl; + mdl != NULL && i == s->bufs_per_mdl; + mdl = cx18_dequeue(s, &s->q_idle)) { + + mdl->id = mdl_id; + + for (i = 0; i < s->bufs_per_mdl; i++) { + if (list_empty(&s->buf_pool)) + break; + + buf = list_first_entry(&s->buf_pool, struct cx18_buffer, + list); + list_move_tail(&buf->list, &mdl->buf_list); + + /* update the firmware's MDL array with this buffer */ + cx18_writel(cx, buf->dma_handle, + &cx->scb->cpu_mdl[mdl_id + i].paddr); + cx18_writel(cx, s->buf_size, + &cx->scb->cpu_mdl[mdl_id + i].length); + } + + if (i == s->bufs_per_mdl) { + /* + * The encoder doesn't honor s->mdl_size. So in the + * case of a non-integral number of buffers to meet + * mdl_size, we lie about the size of the last buffer + * in the MDL to get the encoder to really only send + * us mdl_size bytes per MDL transfer. + */ + partial_buf_size = s->mdl_size % s->buf_size; + if (partial_buf_size) { + cx18_writel(cx, partial_buf_size, + &cx->scb->cpu_mdl[mdl_id + i - 1].length); + } + cx18_enqueue(s, mdl, &s->q_free); + } else { + /* Not enough buffers for this MDL; we won't use it */ + cx18_push(s, mdl, &s->q_idle); + } + mdl_id += i; + } +} + +void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl) +{ + int dma = s->dma; + u32 buf_size = s->buf_size; + struct pci_dev *pci_dev = s->cx->pci_dev; + struct cx18_buffer *buf; + + list_for_each_entry(buf, &mdl->buf_list, list) + pci_dma_sync_single_for_device(pci_dev, buf->dma_handle, + buf_size, dma); } int cx18_stream_alloc(struct cx18_stream *s) @@ -190,44 +352,62 @@ int cx18_stream_alloc(struct cx18_stream *s) if (s->buffers == 0) return 0; - CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n", + CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers " + "(%d.%02d kB total)\n", s->name, s->buffers, s->buf_size, - s->buffers * s->buf_size / 1024); + s->buffers * s->buf_size / 1024, + (s->buffers * s->buf_size * 100 / 1024) % 100); - if (((char __iomem *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] - + if (((char __iomem *)&cx->scb->cpu_mdl[cx->free_mdl_idx + s->buffers] - (char __iomem *)cx->scb) > SCB_RESERVED_SIZE) { unsigned bufsz = (((char __iomem *)cx->scb) + SCB_RESERVED_SIZE - ((char __iomem *)cx->scb->cpu_mdl)); CX18_ERR("Too many buffers, cannot fit in SCB area\n"); CX18_ERR("Max buffers = %zd\n", - bufsz / sizeof(struct cx18_mdl)); + bufsz / sizeof(struct cx18_mdl_ent)); return -ENOMEM; } - s->mdl_offset = cx->mdl_offset; + s->mdl_base_idx = cx->free_mdl_idx; - /* allocate stream buffers. Initially all buffers are in q_free. */ + /* allocate stream buffers and MDLs */ for (i = 0; i < s->buffers; i++) { - struct cx18_buffer *buf = kzalloc(sizeof(struct cx18_buffer), - GFP_KERNEL|__GFP_NOWARN); + struct cx18_mdl *mdl; + struct cx18_buffer *buf; - if (buf == NULL) + /* 1 MDL per buffer to handle the worst & also default case */ + mdl = kzalloc(sizeof(struct cx18_mdl), GFP_KERNEL|__GFP_NOWARN); + if (mdl == NULL) break; + + buf = kzalloc(sizeof(struct cx18_buffer), + GFP_KERNEL|__GFP_NOWARN); + if (buf == NULL) { + kfree(mdl); + break; + } + buf->buf = kmalloc(s->buf_size, GFP_KERNEL|__GFP_NOWARN); if (buf->buf == NULL) { + kfree(mdl); kfree(buf); break; } - buf->id = cx->buffer_id++; + + INIT_LIST_HEAD(&mdl->list); + INIT_LIST_HEAD(&mdl->buf_list); + mdl->id = s->mdl_base_idx; /* a somewhat safe value */ + cx18_enqueue(s, mdl, &s->q_idle); + INIT_LIST_HEAD(&buf->list); buf->dma_handle = pci_map_single(s->cx->pci_dev, buf->buf, s->buf_size, s->dma); cx18_buf_sync_for_cpu(s, buf); - cx18_enqueue(s, buf, &s->q_free); + list_add_tail(&buf->list, &s->buf_pool); } if (i == s->buffers) { - cx->mdl_offset += s->buffers; + cx->free_mdl_idx += s->buffers; return 0; } CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name); @@ -237,13 +417,21 @@ int cx18_stream_alloc(struct cx18_stream *s) void cx18_stream_free(struct cx18_stream *s) { + struct cx18_mdl *mdl; struct cx18_buffer *buf; - /* move all buffers to q_free */ - cx18_flush_queues(s); + /* move all buffers to buf_pool and all MDLs to q_idle */ + cx18_unload_queues(s); + + /* empty q_idle */ + while ((mdl = cx18_dequeue(s, &s->q_idle))) + kfree(mdl); + + /* empty buf_pool */ + while (!list_empty(&s->buf_pool)) { + buf = list_first_entry(&s->buf_pool, struct cx18_buffer, list); + list_del_init(&buf->list); - /* empty q_free */ - while ((buf = cx18_dequeue(s, &s->q_free))) { pci_unmap_single(s->cx->pci_dev, buf->dma_handle, s->buf_size, s->dma); kfree(buf->buf); diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h index 4de06269d88f7..88a6d34ad3bb8 100644 --- a/drivers/media/video/cx18/cx18-queue.h +++ b/drivers/media/video/cx18/cx18-queue.h @@ -40,32 +40,59 @@ static inline void cx18_buf_sync_for_device(struct cx18_stream *s, s->buf_size, s->dma); } +void _cx18_mdl_sync_for_device(struct cx18_stream *s, struct cx18_mdl *mdl); + +static inline void cx18_mdl_sync_for_device(struct cx18_stream *s, + struct cx18_mdl *mdl) +{ + if (list_is_singular(&mdl->buf_list)) + cx18_buf_sync_for_device(s, list_first_entry(&mdl->buf_list, + struct cx18_buffer, + list)); + else + _cx18_mdl_sync_for_device(s, mdl); +} + void cx18_buf_swap(struct cx18_buffer *buf); +void _cx18_mdl_swap(struct cx18_mdl *mdl); + +static inline void cx18_mdl_swap(struct cx18_mdl *mdl) +{ + if (list_is_singular(&mdl->buf_list)) + cx18_buf_swap(list_first_entry(&mdl->buf_list, + struct cx18_buffer, list)); + else + _cx18_mdl_swap(mdl); +} /* cx18_queue utility functions */ -struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *_cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q, int to_front); static inline -struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *cx18_enqueue(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q) { - return _cx18_enqueue(s, buf, q, 0); /* FIFO */ + return _cx18_enqueue(s, mdl, q, 0); /* FIFO */ } static inline -struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_buffer *buf, +struct cx18_queue *cx18_push(struct cx18_stream *s, struct cx18_mdl *mdl, struct cx18_queue *q) { - return _cx18_enqueue(s, buf, q, 1); /* LIFO */ + return _cx18_enqueue(s, mdl, q, 1); /* LIFO */ } void cx18_queue_init(struct cx18_queue *q); -struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); -struct cx18_buffer *cx18_queue_get_buf(struct cx18_stream *s, u32 id, +struct cx18_mdl *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q); +struct cx18_mdl *cx18_queue_get_mdl(struct cx18_stream *s, u32 id, u32 bytesused); void cx18_flush_queues(struct cx18_stream *s); +/* queue MDL reconfiguration helpers */ +void cx18_unload_queues(struct cx18_stream *s); +void cx18_load_queues(struct cx18_stream *s); + /* cx18_stream utility functions */ int cx18_stream_alloc(struct cx18_stream *s); void cx18_stream_free(struct cx18_stream *s); diff --git a/drivers/media/video/cx18/cx18-scb.h b/drivers/media/video/cx18/cx18-scb.h index 1dc1c431f5a14..368f23d08709a 100644 --- a/drivers/media/video/cx18/cx18-scb.h +++ b/drivers/media/video/cx18/cx18-scb.h @@ -81,7 +81,7 @@ /* This structure is used by EPU to provide memory descriptors in its memory */ -struct cx18_mdl { +struct cx18_mdl_ent { u32 paddr; /* Physical address of a buffer segment */ u32 length; /* Length of the buffer segment */ }; @@ -272,7 +272,7 @@ struct cx18_scb { struct cx18_mailbox ppu2epu_mb; struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][CX18_MAX_MDL_ACKS]; - struct cx18_mdl cpu_mdl[1]; + struct cx18_mdl_ent cpu_mdl[1]; }; void cx18_init_scb(struct cx18 *cx); diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 7df513a2dba84..c398651dd74c8 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -115,6 +115,9 @@ static void cx18_stream_init(struct cx18 *cx, int type) s->dma = cx18_stream_info[type].dma; s->buffers = cx->stream_buffers[type]; s->buf_size = cx->stream_buf_size[type]; + INIT_LIST_HEAD(&s->buf_pool); + s->bufs_per_mdl = 1; + s->mdl_size = s->buf_size * s->bufs_per_mdl; init_waitqueue_head(&s->waitq); s->id = -1; @@ -124,6 +127,8 @@ static void cx18_stream_init(struct cx18 *cx, int type) cx18_queue_init(&s->q_busy); spin_lock_init(&s->q_full.lock); cx18_queue_init(&s->q_full); + spin_lock_init(&s->q_idle.lock); + cx18_queue_init(&s->q_idle); INIT_WORK(&s->out_work_order, cx18_out_work_handler); } @@ -257,9 +262,11 @@ static int cx18_reg_dev(struct cx18 *cx, int type) switch (vfl_type) { case VFL_TYPE_GRABBER: - CX18_INFO("Registered device video%d for %s (%d x %d kB)\n", + CX18_INFO("Registered device video%d for %s " + "(%d x %d.%02d kB)\n", num, s->name, cx->stream_buffers[type], - cx->stream_buf_size[type]/1024); + cx->stream_buf_size[type] / 1024, + (cx->stream_buf_size[type] * 100 / 1024) % 100); break; case VFL_TYPE_RADIO: @@ -441,8 +448,8 @@ static void cx18_vbi_setup(struct cx18_stream *s) } static -struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf) +struct cx18_queue *_cx18_stream_put_mdl_fw(struct cx18_stream *s, + struct cx18_mdl *mdl) { struct cx18 *cx = s->cx; struct cx18_queue *q; @@ -451,16 +458,16 @@ struct cx18_queue *_cx18_stream_put_buf_fw(struct cx18_stream *s, if (s->handle == CX18_INVALID_TASK_HANDLE || test_bit(CX18_F_S_STOPPING, &s->s_flags) || !test_bit(CX18_F_S_STREAMING, &s->s_flags)) - return cx18_enqueue(s, buf, &s->q_free); + return cx18_enqueue(s, mdl, &s->q_free); - q = cx18_enqueue(s, buf, &s->q_busy); + q = cx18_enqueue(s, mdl, &s->q_busy); if (q != &s->q_busy) - return q; /* The firmware has the max buffers it can handle */ + return q; /* The firmware has the max MDLs it can handle */ - cx18_buf_sync_for_device(s, buf); + cx18_mdl_sync_for_device(s, mdl); cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle, - (void __iomem *) &cx->scb->cpu_mdl[buf->id] - cx->enc_mem, - 1, buf->id, s->buf_size); + (void __iomem *) &cx->scb->cpu_mdl[mdl->id] - cx->enc_mem, + s->bufs_per_mdl, mdl->id, s->mdl_size); return q; } @@ -468,19 +475,19 @@ static void _cx18_stream_load_fw_queue(struct cx18_stream *s) { struct cx18_queue *q; - struct cx18_buffer *buf; + struct cx18_mdl *mdl; - if (atomic_read(&s->q_free.buffers) == 0 || - atomic_read(&s->q_busy.buffers) >= CX18_MAX_FW_MDLS_PER_STREAM) + if (atomic_read(&s->q_free.depth) == 0 || + atomic_read(&s->q_busy.depth) >= CX18_MAX_FW_MDLS_PER_STREAM) return; /* Move from q_free to q_busy notifying the firmware, until the limit */ do { - buf = cx18_dequeue(s, &s->q_free); - if (buf == NULL) + mdl = cx18_dequeue(s, &s->q_free); + if (mdl == NULL) break; - q = _cx18_stream_put_buf_fw(s, buf); - } while (atomic_read(&s->q_busy.buffers) < CX18_MAX_FW_MDLS_PER_STREAM + q = _cx18_stream_put_mdl_fw(s, mdl); + } while (atomic_read(&s->q_busy.depth) < CX18_MAX_FW_MDLS_PER_STREAM && q == &s->q_busy); } @@ -492,11 +499,51 @@ void cx18_out_work_handler(struct work_struct *work) _cx18_stream_load_fw_queue(s); } +static void cx18_stream_configure_mdls(struct cx18_stream *s) +{ + cx18_unload_queues(s); + + switch (s->type) { + case CX18_ENC_STREAM_TYPE_YUV: + /* + * Height should be a multiple of 32 lines. + * Set the MDL size to the exact size needed for one frame. + * Use enough buffers per MDL to cover the MDL size + */ + s->mdl_size = 720 * s->cx->params.height * 3 / 2; + s->bufs_per_mdl = s->mdl_size / s->buf_size; + if (s->mdl_size % s->buf_size) + s->bufs_per_mdl++; + break; + case CX18_ENC_STREAM_TYPE_VBI: + s->bufs_per_mdl = 1; + if (cx18_raw_vbi(s->cx)) { + s->mdl_size = (s->cx->is_60hz ? 12 : 18) + * 2 * vbi_active_samples; + } else { + /* + * See comment in cx18_vbi_setup() below about the + * extra lines we capture in sliced VBI mode due to + * the lines on which EAV RP codes toggle. + */ + s->mdl_size = s->cx->is_60hz + ? (21 - 4 + 1) * 2 * vbi_hblank_samples_60Hz + : (23 - 2 + 1) * 2 * vbi_hblank_samples_50Hz; + } + break; + default: + s->bufs_per_mdl = 1; + s->mdl_size = s->buf_size * s->bufs_per_mdl; + break; + } + + cx18_load_queues(s); +} + int cx18_start_v4l2_encode_stream(struct cx18_stream *s) { u32 data[MAX_MB_ARGUMENTS]; struct cx18 *cx = s->cx; - struct cx18_buffer *buf; int captype = 0; struct cx18_api_func_private priv; @@ -619,14 +666,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s) (void __iomem *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem); /* Init all the cpu_mdls for this stream */ - cx18_flush_queues(s); - spin_lock(&s->q_free.lock); - list_for_each_entry(buf, &s->q_free.list, list) { - cx18_writel(cx, buf->dma_handle, - &cx->scb->cpu_mdl[buf->id].paddr); - cx18_writel(cx, s->buf_size, &cx->scb->cpu_mdl[buf->id].length); - } - spin_unlock(&s->q_free.lock); + cx18_stream_configure_mdls(s); _cx18_stream_load_fw_queue(s); /* begin_capture */ diff --git a/drivers/media/video/cx18/cx18-streams.h b/drivers/media/video/cx18/cx18-streams.h index 1afc3fd9d8225..4a01db5e5a352 100644 --- a/drivers/media/video/cx18/cx18-streams.h +++ b/drivers/media/video/cx18/cx18-streams.h @@ -28,18 +28,18 @@ int cx18_streams_setup(struct cx18 *cx); int cx18_streams_register(struct cx18 *cx); void cx18_streams_cleanup(struct cx18 *cx, int unregister); -/* Related to submission of buffers to firmware */ +/* Related to submission of mdls to firmware */ static inline void cx18_stream_load_fw_queue(struct cx18_stream *s) { struct cx18 *cx = s->cx; queue_work(cx->out_work_queue, &s->out_work_order); } -static inline void cx18_stream_put_buf_fw(struct cx18_stream *s, - struct cx18_buffer *buf) +static inline void cx18_stream_put_mdl_fw(struct cx18_stream *s, + struct cx18_mdl *mdl) { - /* Put buf on q_free; the out work handler will move buf(s) to q_busy */ - cx18_enqueue(s, buf, &s->q_free); + /* Put mdl on q_free; the out work handler will move mdl(s) to q_busy */ + cx18_enqueue(s, mdl, &s->q_free); cx18_stream_load_fw_queue(s); } diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c index c2aef4add31da..574c1c6974f8d 100644 --- a/drivers/media/video/cx18/cx18-vbi.c +++ b/drivers/media/video/cx18/cx18-vbi.c @@ -105,6 +105,7 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp) /* Compress raw VBI format, removes leading SAV codes and surplus space after the frame. Returns new compressed size. */ +/* FIXME - this function ignores the input size. */ static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size) { u32 line_size = vbi_active_samples; @@ -185,8 +186,7 @@ static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size, return line; } -void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, - int streamtype) +static void _cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf) { /* * The CX23418 provides a 12 byte header in its raw VBI buffers to us: @@ -203,9 +203,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, u32 pts; int lines; - if (streamtype != CX18_ENC_STREAM_TYPE_VBI) - return; - /* * The CX23418 sends us data that is 32 bit little-endian swapped, * but we want the raw VBI bytes in the order they were in the raster @@ -250,3 +247,31 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, copy_vbi_data(cx, lines, pts); cx->vbi.frame++; } + +void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, + int streamtype) +{ + struct cx18_buffer *buf; + u32 orig_used; + + if (streamtype != CX18_ENC_STREAM_TYPE_VBI) + return; + + /* + * Big assumption here: + * Every buffer hooked to the MDL's buf_list is a complete VBI frame + * that ends at the end of the buffer. + * + * To assume anything else would make the code in this file + * more complex, or require extra memcpy()'s to make the + * buffers satisfy the above assumption. It's just simpler to set + * up the encoder buffer transfers to make the assumption true. + */ + list_for_each_entry(buf, &mdl->buf_list, list) { + orig_used = buf->bytesused; + if (orig_used == 0) + break; + _cx18_process_vbi_data(cx, buf); + mdl->bytesused -= (orig_used - buf->bytesused); + } +} diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h index e7e1ae427f345..b365cf4b46683 100644 --- a/drivers/media/video/cx18/cx18-vbi.h +++ b/drivers/media/video/cx18/cx18-vbi.h @@ -21,6 +21,6 @@ * 02111-1307 USA */ -void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf, +void cx18_process_vbi_data(struct cx18 *cx, struct cx18_mdl *mdl, int streamtype); int cx18_used_line(struct cx18 *cx, int line, int field); diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h index 45494b094e7fc..9c0b5bb1b019d 100644 --- a/drivers/media/video/cx18/cx18-version.h +++ b/drivers/media/video/cx18/cx18-version.h @@ -24,7 +24,7 @@ #define CX18_DRIVER_NAME "cx18" #define CX18_DRIVER_VERSION_MAJOR 1 -#define CX18_DRIVER_VERSION_MINOR 2 +#define CX18_DRIVER_VERSION_MINOR 3 #define CX18_DRIVER_VERSION_PATCHLEVEL 0 #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL) diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h index 9956abf576c59..868806effdcf2 100644 --- a/drivers/media/video/cx18/cx23418.h +++ b/drivers/media/video/cx18/cx23418.h @@ -363,7 +363,7 @@ /* Description: This command provides the offset to a Memory Descriptor List IN[0] - Task handle. Handle of the task to start IN[1] - Offset of the MDL from the beginning of the local DDR. - IN[2] - Number of cx18_mdl structures in the array pointed to by IN[1] + IN[2] - Number of cx18_mdl_ent structures in the array pointed to by IN[1] IN[3] - Buffer ID IN[4] - Total buffer length ReturnCode - One of the ERR_DE_... */ diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c index 48f22fa38e6cc..cd135f01b9c18 100644 --- a/drivers/media/video/cx231xx/cx231xx-input.c +++ b/drivers/media/video/cx231xx/cx231xx-input.c @@ -126,8 +126,7 @@ static void cx231xx_ir_handle_key(struct cx231xx_IR *ir) if (do_sendkey) { dprintk("sending keypress\n"); - ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], - poll_result.rc_data[0]); + ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0]); ir_input_nokey(ir->input, &ir->ir); } @@ -198,7 +197,11 @@ int cx231xx_ir_init(struct cx231xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, + dev->board.ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; @@ -223,6 +226,7 @@ int cx231xx_ir_init(struct cx231xx *dev) cx231xx_ir_stop(ir); dev->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -237,6 +241,7 @@ int cx231xx_ir_fini(struct cx231xx *dev) return 0; cx231xx_ir_stop(ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 36503725d9735..d095aa0d6d19f 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -2106,7 +2106,7 @@ static int cx231xx_v4l2_close(struct file *filp) } /* Save some power by putting tuner to sleep */ - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); /* do this before setting alternate! */ cx231xx_uninit_isoc(dev); diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index fd3fc3e3198a7..bcdda9a9aa962 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -18,7 +18,9 @@ config VIDEO_CX23885 select DVB_TDA10048 if !DVB_FE_CUSTOMISE select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_STV6110 if !DVB_FE_CUSTOMISE + select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_DS3000 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile index ab8ea35c9bfbb..5787ae243631f 100644 --- a/drivers/media/video/cx23885/Makefile +++ b/drivers/media/video/cx23885/Makefile @@ -1,6 +1,7 @@ cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \ - netup-init.o cimax2.o netup-eeprom.o + cx23885-ioctl.o cx23885-ir.o cx23885-input.o cx23888-ir.o \ + netup-init.o cimax2.o netup-eeprom.o cx23885-f300.o obj-$(CONFIG_VIDEO_CX23885) += cx23885.o diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c index 6c3b51ce3372f..0eed852c61e9c 100644 --- a/drivers/media/video/cx23885/cx23885-417.c +++ b/drivers/media/video/cx23885/cx23885-417.c @@ -37,6 +37,7 @@ #include #include "cx23885.h" +#include "cx23885-ioctl.h" #define CX23885_FIRM_IMAGE_SIZE 376836 #define CX23885_FIRM_IMAGE_NAME "v4l-cx23885-enc.fw" @@ -318,7 +319,7 @@ static int mc417_wait_ready(struct cx23885_dev *dev) } } -static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) +int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) { u32 regval; @@ -382,7 +383,7 @@ static int mc417_register_write(struct cx23885_dev *dev, u16 address, u32 value) return mc417_wait_ready(dev); } -static int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) +int mc417_register_read(struct cx23885_dev *dev, u16 address, u32 *value) { int retval; u32 regval; @@ -1724,6 +1725,11 @@ static const struct v4l2_ioctl_ops mpeg_ioctl_ops = { .vidioc_log_status = vidioc_log_status, .vidioc_querymenu = vidioc_querymenu, .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_chip_ident = cx23885_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = cx23885_g_register, + .vidioc_s_register = cx23885_s_register, +#endif }; static struct video_device cx23885_mpeg_template = { diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index bfdf79f1033c2..1ec48169277d9 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -28,6 +28,7 @@ #include "cx23885.h" #include "tuner-xc2028.h" #include "netup-init.h" +#include "cx23888-ir.h" /* ------------------------------------------------------------------ */ /* board config info */ @@ -199,11 +200,61 @@ struct cx23885_board cx23885_boards[] = { }, [CX23885_BOARD_MYGICA_X8506] = { .name = "Mygica X8506 DMB-TH", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, + .input = { + { + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_COMPOSITE2, + }, + { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_COMPOSITE8, + }, + { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_SVIDEO_LUMA3 | + CX25840_SVIDEO_CHROMA4, + }, + { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_COMPONENT_ON | + CX25840_VIN1_CH1 | + CX25840_VIN6_CH2 | + CX25840_VIN7_CH3, + }, + }, }, [CX23885_BOARD_MAGICPRO_PROHDTVE2] = { .name = "Magic-Pro ProHDTV Extreme 2", + .tuner_type = TUNER_XC5000, + .tuner_addr = 0x61, + .porta = CX23885_ANALOG_VIDEO, .portb = CX23885_MPEG_DVB, + .input = { + { + .type = CX23885_VMUX_TELEVISION, + .vmux = CX25840_COMPOSITE2, + }, + { + .type = CX23885_VMUX_COMPOSITE1, + .vmux = CX25840_COMPOSITE8, + }, + { + .type = CX23885_VMUX_SVIDEO, + .vmux = CX25840_SVIDEO_LUMA3 | + CX25840_SVIDEO_CHROMA4, + }, + { + .type = CX23885_VMUX_COMPONENT, + .vmux = CX25840_COMPONENT_ON | + CX25840_VIN1_CH1 | + CX25840_VIN6_CH2 | + CX25840_VIN7_CH3, + }, + }, }, [CX23885_BOARD_HAUPPAUGE_HVR1850] = { .name = "Hauppauge WinTV-HVR1850", @@ -214,6 +265,15 @@ struct cx23885_board cx23885_boards[] = { .name = "Compro VideoMate E800", .portc = CX23885_MPEG_DVB, }, + [CX23885_BOARD_HAUPPAUGE_HVR1290] = { + .name = "Hauppauge WinTV-HVR1290", + .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_MYGICA_X8558PRO] = { + .name = "Mygica X8558 PRO DMB-TH", + .portb = CX23885_MPEG_DVB, + .portc = CX23885_MPEG_DVB, + }, }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -349,6 +409,14 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x1858, .subdevice = 0xe800, .card = CX23885_BOARD_COMPRO_VIDEOMATE_E800, + }, { + .subvendor = 0x0070, + .subdevice = 0x8551, + .card = CX23885_BOARD_HAUPPAUGE_HVR1290, + }, { + .subvendor = 0x14f1, + .subdevice = 0x8578, + .card = CX23885_BOARD_MYGICA_X8558PRO, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -509,9 +577,13 @@ static void hauppauge_eeprom(struct cx23885_dev *dev, u8 *eeprom_data) * DVB-T and MPEG2 HW Encoder */ break; case 85021: - /* WinTV-HVR1850 (PCIe, OEM, RCA in, IR, FM, + /* WinTV-HVR1850 (PCIe, Retail, 3.5mm in, IR, FM, Dual channel ATSC and MPEG2 HW Encoder */ break; + case 85721: + /* WinTV-HVR1290 (PCIe, OEM, RCA in, IR, + Dual channel ATSC and Basic analog */ + break; default: printk(KERN_WARNING "%s: warning: " "unknown hauppauge model #%d\n", @@ -710,10 +782,14 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) cx_set(GP0_IO, 0x00040004); break; case CX23885_BOARD_TBS_6920: - case CX23885_BOARD_TEVII_S470: cx_write(MC417_CTL, 0x00000036); cx_write(MC417_OEN, 0x00001000); - cx_write(MC417_RWD, 0x00001800); + cx_set(MC417_RWD, 0x00000002); + mdelay(200); + cx_clear(MC417_RWD, 0x00000800); + mdelay(200); + cx_set(MC417_RWD, 0x00000800); + mdelay(200); break; case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: /* GPIO-0 INTA from CiMax1 @@ -758,15 +834,26 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) break; case CX23885_BOARD_MYGICA_X8506: case CX23885_BOARD_MAGICPRO_PROHDTVE2: + /* GPIO-0 (0)Analog / (1)Digital TV */ /* GPIO-1 reset XC5000 */ /* GPIO-2 reset LGS8GL5 / LGS8G75 */ - cx_set(GP0_IO, 0x00060000); - cx_clear(GP0_IO, 0x00000006); + cx23885_gpio_enable(dev, GPIO_0 | GPIO_1 | GPIO_2, 1); + cx23885_gpio_clear(dev, GPIO_1 | GPIO_2); mdelay(100); - cx_set(GP0_IO, 0x00060006); + cx23885_gpio_set(dev, GPIO_0 | GPIO_1 | GPIO_2); + mdelay(100); + break; + case CX23885_BOARD_MYGICA_X8558PRO: + /* GPIO-0 reset first ATBM8830 */ + /* GPIO-1 reset second ATBM8830 */ + cx23885_gpio_enable(dev, GPIO_0 | GPIO_1, 1); + cx23885_gpio_clear(dev, GPIO_0 | GPIO_1); + mdelay(100); + cx23885_gpio_set(dev, GPIO_0 | GPIO_1); mdelay(100); break; case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: /* GPIO-0 656_CLK */ /* GPIO-1 656_D0 */ /* GPIO-2 Wake# */ @@ -801,6 +888,7 @@ void cx23885_gpio_setup(struct cx23885_dev *dev) int cx23885_ir_init(struct cx23885_dev *dev) { + int ret = 0; switch (dev->board) { case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: @@ -812,15 +900,46 @@ int cx23885_ir_init(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1275: case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: - case CX23885_BOARD_HAUPPAUGE_HVR1850: /* FIXME: Implement me */ break; + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + ret = cx23888_ir_probe(dev); + if (ret) + break; + dev->sd_ir = cx23885_find_hw(dev, CX23885_HW_888_IR); + dev->pci_irqmask |= PCI_MSK_IR; + break; case CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP: request_module("ir-kbd-i2c"); break; } - return 0; + return ret; +} + +void cx23885_ir_fini(struct cx23885_dev *dev) +{ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + dev->pci_irqmask &= ~PCI_MSK_IR; + cx_clear(PCI_INT_MSK, PCI_MSK_IR); + cx23888_ir_remove(dev); + dev->sd_ir = NULL; + break; + } +} + +void cx23885_ir_pci_int_enable(struct cx23885_dev *dev) +{ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + if (dev->sd_ir && (dev->pci_irqmask & PCI_MSK_IR)) + cx_set(PCI_INT_MSK, PCI_MSK_IR); + break; + } } void cx23885_card_setup(struct cx23885_dev *dev) @@ -853,6 +972,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1255: case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: if (dev->i2c_bus[0].i2c_rc == 0) hauppauge_eeprom(dev, eeprom+0xc0); break; @@ -886,8 +1006,12 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; - case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_TBS_6920: + ts1->gen_ctrl_val = 0x4; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; + case CX23885_BOARD_TEVII_S470: case CX23885_BOARD_DVBWORLD_2005: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -907,6 +1031,14 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; + case CX23885_BOARD_MYGICA_X8558PRO: + ts1->gen_ctrl_val = 0x5; /* Parallel */ + ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ + ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ + ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; + break; case CX23885_BOARD_HAUPPAUGE_HVR1250: case CX23885_BOARD_HAUPPAUGE_HVR1500: case CX23885_BOARD_HAUPPAUGE_HVR1500Q: @@ -922,6 +1054,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_HAUPPAUGE_HVR1210: case CX23885_BOARD_HAUPPAUGE_HVR1850: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_HAUPPAUGE_HVR1290: default: ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */ @@ -939,6 +1072,10 @@ void cx23885_card_setup(struct cx23885_dev *dev) case CX23885_BOARD_COMPRO_VIDEOMATE_E650F: case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: case CX23885_BOARD_COMPRO_VIDEOMATE_E800: + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + case CX23885_BOARD_HAUPPAUGE_HVR1290: dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_bus[2].i2c_adap, "cx25840", "cx25840", 0x88 >> 1, NULL); diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index fa2d350e20fdb..04b12d27bc139 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -32,6 +32,9 @@ #include "cx23885.h" #include "cimax2.h" +#include "cx23888-ir.h" +#include "cx23885-ir.h" +#include "cx23885-input.h" MODULE_DESCRIPTION("Driver for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth "); @@ -753,6 +756,23 @@ static void cx23885_dev_checkrevision(struct cx23885_dev *dev) __func__, dev->hwrevision); } +/* Find the first v4l2_subdev member of the group id in hw */ +struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw) +{ + struct v4l2_subdev *result = NULL; + struct v4l2_subdev *sd; + + spin_lock(&dev->v4l2_dev.lock); + v4l2_device_for_each_subdev(sd, &dev->v4l2_dev) { + if (sd->grp_id == hw) { + result = sd; + break; + } + } + spin_unlock(&dev->v4l2_dev.lock); + return result; +} + static int cx23885_dev_setup(struct cx23885_dev *dev) { int i; @@ -899,7 +919,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) cx23885_i2c_register(&dev->i2c_bus[1]); cx23885_i2c_register(&dev->i2c_bus[2]); cx23885_card_setup(dev); - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); cx23885_ir_init(dev); if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) { @@ -1637,6 +1657,7 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) u32 ts1_status, ts1_mask; u32 ts2_status, ts2_mask; int vida_count = 0, ts1_count = 0, ts2_count = 0, handled = 0; + bool ir_handled = false; pci_status = cx_read(PCI_INT_STAT); pci_mask = cx_read(PCI_INT_MSK); @@ -1662,18 +1683,12 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) dprintk(7, "ts2_status: 0x%08x ts2_mask: 0x%08x count: 0x%x\n", ts2_status, ts2_mask, ts2_count); - if ((pci_status & PCI_MSK_RISC_RD) || - (pci_status & PCI_MSK_RISC_WR) || - (pci_status & PCI_MSK_AL_RD) || - (pci_status & PCI_MSK_AL_WR) || - (pci_status & PCI_MSK_APB_DMA) || - (pci_status & PCI_MSK_VID_C) || - (pci_status & PCI_MSK_VID_B) || - (pci_status & PCI_MSK_VID_A) || - (pci_status & PCI_MSK_AUD_INT) || - (pci_status & PCI_MSK_AUD_EXT) || - (pci_status & PCI_MSK_GPIO0) || - (pci_status & PCI_MSK_GPIO1)) { + if (pci_status & (PCI_MSK_RISC_RD | PCI_MSK_RISC_WR | + PCI_MSK_AL_RD | PCI_MSK_AL_WR | PCI_MSK_APB_DMA | + PCI_MSK_VID_C | PCI_MSK_VID_B | PCI_MSK_VID_A | + PCI_MSK_AUD_INT | PCI_MSK_AUD_EXT | + PCI_MSK_GPIO0 | PCI_MSK_GPIO1 | + PCI_MSK_IR)) { if (pci_status & PCI_MSK_RISC_RD) dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n", @@ -1722,6 +1737,10 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (pci_status & PCI_MSK_GPIO1) dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n", PCI_MSK_GPIO1); + + if (pci_status & PCI_MSK_IR) + dprintk(7, " (PCI_MSK_IR 0x%08x)\n", + PCI_MSK_IR); } if (cx23885_boards[dev->board].cimax > 0 && @@ -1752,12 +1771,48 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id) if (vida_status) handled += cx23885_video_irq(dev, vida_status); + if (pci_status & PCI_MSK_IR) { + v4l2_subdev_call(dev->sd_ir, ir, interrupt_service_routine, + pci_status, &ir_handled); + if (ir_handled) + handled++; + } + if (handled) cx_write(PCI_INT_STAT, pci_status); out: return IRQ_RETVAL(handled); } +static void cx23885_v4l2_dev_notify(struct v4l2_subdev *sd, + unsigned int notification, void *arg) +{ + struct cx23885_dev *dev; + + if (sd == NULL) + return; + + dev = to_cx23885(sd->v4l2_dev); + + switch (notification) { + case V4L2_SUBDEV_IR_RX_NOTIFY: /* Called in an IRQ context */ + if (sd == dev->sd_ir) + cx23885_ir_rx_v4l2_dev_notify(sd, *(u32 *)arg); + break; + case V4L2_SUBDEV_IR_TX_NOTIFY: /* Called in an IRQ context */ + if (sd == dev->sd_ir) + cx23885_ir_tx_v4l2_dev_notify(sd, *(u32 *)arg); + break; + } +} + +static void cx23885_v4l2_dev_notify_init(struct cx23885_dev *dev) +{ + INIT_WORK(&dev->ir_rx_work, cx23885_ir_rx_work_handler); + INIT_WORK(&dev->ir_tx_work, cx23885_ir_tx_work_handler); + dev->v4l2_dev.notify = cx23885_v4l2_dev_notify; +} + static inline int encoder_on_portb(struct cx23885_dev *dev) { return cx23885_boards[dev->board].portb == CX23885_MPEG_ENCODER; @@ -1816,6 +1871,26 @@ void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask) printk(KERN_INFO "%s: Unsupported\n", dev->name); } +u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask) +{ + if (mask & 0x00000007) + return (cx_read(GP0_IO) >> 8) & mask & 0x7; + + if (mask & 0x0007fff8) { + if (encoder_on_portb(dev) || encoder_on_portc(dev)) + printk(KERN_ERR + "%s: Reading GPIO moving on encoder ports\n", + dev->name); + return (cx_read(MC417_RWD) & ((mask & 0x7fff8) >> 3)) << 3; + } + + /* TODO: 23-19 */ + if (mask & 0x00f80000) + printk(KERN_INFO "%s: Unsupported\n", dev->name); + + return 0; +} + void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput) { if ((mask & 0x00000007) && asoutput) @@ -1854,6 +1929,9 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, if (err < 0) goto fail_free; + /* Prepare to handle notifications from subdevices */ + cx23885_v4l2_dev_notify_init(dev); + /* pci init */ dev->pci = pci_dev; if (pci_enable_device(pci_dev)) { @@ -1896,6 +1974,14 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev, break; } + /* + * The CX2388[58] IR controller can start firing interrupts when + * enabled, so these have to take place after the cx23885_irq() handler + * is hooked up by the call to request_irq() above. + */ + cx23885_ir_pci_int_enable(dev); + cx23885_input_init(dev); + return 0; fail_irq: @@ -1912,6 +1998,9 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev) struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); struct cx23885_dev *dev = to_cx23885(v4l2_dev); + cx23885_input_fini(dev); + cx23885_ir_fini(dev); + cx23885_shutdown(dev); pci_disable_device(pci_dev); diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 16c6a921f40b4..e45d2df081380 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -38,6 +38,7 @@ #include "tda18271.h" #include "lgdt330x.h" #include "xc5000.h" +#include "max2165.h" #include "tda10048.h" #include "tuner-xc2028.h" #include "tuner-simple.h" @@ -54,6 +55,9 @@ #include "netup-eeprom.h" #include "netup-init.h" #include "lgdt3305.h" +#include "atbm8830.h" +#include "ds3000.h" +#include "cx23885-f300.h" static unsigned int debug; @@ -400,6 +404,7 @@ static struct stv0900_reg stv0900_ts_regs[] = { static struct stv0900_config netup_stv0900_config = { .demod_address = 0x68, + .demod_mode = 1, /* dual */ .xtal = 8000000, .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ .diseqc_mode = 2,/* 2/3 PWM */ @@ -414,34 +419,22 @@ static struct stv6110_config netup_stv6110_tunerconfig_a = { .i2c_address = 0x60, .mclk = 16000000, .clk_div = 1, + .gain = 8, /* +16 dB - maximum gain */ }; static struct stv6110_config netup_stv6110_tunerconfig_b = { .i2c_address = 0x63, .mclk = 16000000, .clk_div = 1, + .gain = 8, /* +16 dB - maximum gain */ }; -static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) -{ - struct cx23885_tsport *port = fe->dvb->priv; - struct cx23885_dev *dev = port->dev; - - if (voltage == SEC_VOLTAGE_18) - cx_write(MC417_RWD, 0x00001e00);/* GPIO-13 high */ - else if (voltage == SEC_VOLTAGE_13) - cx_write(MC417_RWD, 0x00001a00);/* GPIO-13 low */ - else - cx_write(MC417_RWD, 0x00001800);/* GPIO-12 low */ - return 0; -} - static struct cx24116_config tbs_cx24116_config = { - .demod_address = 0x05, + .demod_address = 0x55, }; -static struct cx24116_config tevii_cx24116_config = { - .demod_address = 0x55, +static struct ds3000_config tevii_ds3000_config = { + .demod_address = 0x68, }; static struct cx24116_config dvbworld_cx24116_config = { @@ -486,11 +479,40 @@ static int cx23885_dvb_set_frontend(struct dvb_frontend *fe, break; } break; + case CX23885_BOARD_MYGICA_X8506: + case CX23885_BOARD_MAGICPRO_PROHDTVE2: + /* Select Digital TV */ + cx23885_gpio_set(dev, GPIO_0); + break; } - return (port->set_frontend_save) ? - port->set_frontend_save(fe, param) : -ENODEV; + return 0; } +static int cx23885_dvb_fe_ioctl_override(struct dvb_frontend *fe, + unsigned int cmd, void *parg, + unsigned int stage) +{ + int err = 0; + + switch (stage) { + case DVB_FE_IOCTL_PRE: + + switch (cmd) { + case FE_SET_FRONTEND: + err = cx23885_dvb_set_frontend(fe, + (struct dvb_frontend_parameters *) parg); + break; + } + break; + + case DVB_FE_IOCTL_POST: + /* no post-ioctl handling required */ + break; + } + return err; +}; + + static struct lgs8gxx_config magicpro_prohdtve2_lgs8g75_config = { .prod = LGS8GXX_PROD_LGS8G75, .demod_address = 0x19, @@ -511,6 +533,38 @@ static struct xc5000_config magicpro_prohdtve2_xc5000_config = { .if_khz = 6500, }; +static struct atbm8830_config mygica_x8558pro_atbm8830_cfg1 = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x44, + .serial_ts = 0, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, +}; + +static struct max2165_config mygic_x8558pro_max2165_cfg1 = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + +static struct atbm8830_config mygica_x8558pro_atbm8830_cfg2 = { + .prod = ATBM8830_PROD_8830, + .demod_address = 0x44, + .serial_ts = 1, + .ts_sampling_edge = 1, + .ts_clk_gated = 0, + .osc_clk_freq = 30400, /* in kHz */ + .if_freq = 0, /* zero IF */ + .zif_swap_iq = 1, +}; + +static struct max2165_config mygic_x8558pro_max2165_cfg2 = { + .i2c_address = 0x60, + .osc_clk = 20 +}; + static int dvb_register(struct cx23885_tsport *port) { struct cx23885_dev *dev = port->dev; @@ -550,12 +604,6 @@ static int dvb_register(struct cx23885_tsport *port) 0x60, &dev->i2c_bus[1].i2c_adap, &hauppauge_hvr127x_config); } - - /* FIXME: temporary hack */ - /* define bridge override to set_frontend */ - port->set_frontend_save = fe0->dvb.frontend->ops.set_frontend; - fe0->dvb.frontend->ops.set_frontend = cx23885_dvb_set_frontend; - break; case CX23885_BOARD_HAUPPAUGE_HVR1255: i2c_bus = &dev->i2c_bus[0]; @@ -772,23 +820,23 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_TBS_6920: - i2c_bus = &dev->i2c_bus[0]; + i2c_bus = &dev->i2c_bus[1]; fe0->dvb.frontend = dvb_attach(cx24116_attach, - &tbs_cx24116_config, - &i2c_bus->i2c_adap); + &tbs_cx24116_config, + &i2c_bus->i2c_adap); if (fe0->dvb.frontend != NULL) - fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; break; case CX23885_BOARD_TEVII_S470: i2c_bus = &dev->i2c_bus[1]; - fe0->dvb.frontend = dvb_attach(cx24116_attach, - &tevii_cx24116_config, - &i2c_bus->i2c_adap); + fe0->dvb.frontend = dvb_attach(ds3000_attach, + &tevii_ds3000_config, + &i2c_bus->i2c_adap); if (fe0->dvb.frontend != NULL) - fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage; + fe0->dvb.frontend->ops.set_voltage = f300_set_voltage; break; case CX23885_BOARD_DVBWORLD_2005: @@ -814,8 +862,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, - LNBH24_TTX, 0x09)) + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x09)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -835,8 +883,8 @@ static int dvb_register(struct cx23885_tsport *port) if (!dvb_attach(lnbh24_attach, fe0->dvb.frontend, &i2c_bus->i2c_adap, - LNBH24_PCL, - LNBH24_TTX, 0x0a)) + LNBH24_PCL | LNBH24_TTX, + LNBH24_TEN, 0x0a)) printk(KERN_ERR "No LNBH24 found!\n"); @@ -872,6 +920,7 @@ static int dvb_register(struct cx23885_tsport *port) } break; case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: i2c_bus = &dev->i2c_bus[0]; fe0->dvb.frontend = dvb_attach(s5h1411_attach, &hcw_s5h1411_config, @@ -881,6 +930,36 @@ static int dvb_register(struct cx23885_tsport *port) 0x60, &dev->i2c_bus[0].i2c_adap, &hauppauge_tda18271_config); break; + case CX23885_BOARD_MYGICA_X8558PRO: + switch (port->nr) { + /* port B */ + case 1: + i2c_bus = &dev->i2c_bus[0]; + fe0->dvb.frontend = dvb_attach(atbm8830_attach, + &mygica_x8558pro_atbm8830_cfg1, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(max2165_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &mygic_x8558pro_max2165_cfg1); + } + break; + /* port C */ + case 2: + i2c_bus = &dev->i2c_bus[1]; + fe0->dvb.frontend = dvb_attach(atbm8830_attach, + &mygica_x8558pro_atbm8830_cfg2, + &i2c_bus->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(max2165_attach, + fe0->dvb.frontend, + &i2c_bus->i2c_adap, + &mygic_x8558pro_max2165_cfg2); + } + break; + } + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " @@ -897,14 +976,15 @@ static int dvb_register(struct cx23885_tsport *port) fe0->dvb.frontend->callback = cx23885_tuner_callback; /* Put the analog decoder in standby to keep it quiet */ - call_all(dev, tuner, s_standby); + call_all(dev, core, s_power, 0); if (fe0->dvb.frontend->ops.analog_ops.standby) fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend); /* register everything */ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port, - &dev->pci->dev, adapter_nr, 0); + &dev->pci->dev, adapter_nr, 0, + cx23885_dvb_fe_ioctl_override); /* init CI & MAC */ switch (dev->board) { diff --git a/drivers/media/video/cx23885/cx23885-f300.c b/drivers/media/video/cx23885/cx23885-f300.c new file mode 100644 index 0000000000000..93998f2209865 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-f300.c @@ -0,0 +1,177 @@ +/* + * Driver for Silicon Labs C8051F300 microcontroller. + * + * It is used for LNB power control in TeVii S470, + * TBS 6920 PCIe DVB-S2 cards. + * + * Microcontroller connected to cx23885 GPIO pins: + * GPIO0 - data - P0.3 F300 + * GPIO1 - reset - P0.2 F300 + * GPIO2 - clk - P0.1 F300 + * GPIO3 - busy - P0.0 F300 + * + * Copyright (C) 2009 Igor M. Liplianin + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx23885.h" + +#define F300_DATA GPIO_0 +#define F300_RESET GPIO_1 +#define F300_CLK GPIO_2 +#define F300_BUSY GPIO_3 + +static void f300_set_line(struct cx23885_dev *dev, u32 line, u8 lvl) +{ + cx23885_gpio_enable(dev, line, 1); + if (lvl == 1) + cx23885_gpio_set(dev, line); + else + cx23885_gpio_clear(dev, line); +} + +static u8 f300_get_line(struct cx23885_dev *dev, u32 line) +{ + cx23885_gpio_enable(dev, line, 0); + + return cx23885_gpio_get(dev, line); +} + +static void f300_send_byte(struct cx23885_dev *dev, u8 dta) +{ + u8 i; + + for (i = 0; i < 8; i++) { + f300_set_line(dev, F300_CLK, 0); + udelay(30); + f300_set_line(dev, F300_DATA, (dta & 0x80) >> 7);/* msb first */ + udelay(30); + dta <<= 1; + f300_set_line(dev, F300_CLK, 1); + udelay(30); + } +} + +static u8 f300_get_byte(struct cx23885_dev *dev) +{ + u8 i, dta = 0; + + for (i = 0; i < 8; i++) { + f300_set_line(dev, F300_CLK, 0); + udelay(30); + dta <<= 1; + f300_set_line(dev, F300_CLK, 1); + udelay(30); + dta |= f300_get_line(dev, F300_DATA);/* msb first */ + + } + + return dta; +} + +static u8 f300_xfer(struct dvb_frontend *fe, u8 *buf) +{ + struct cx23885_tsport *port = fe->dvb->priv; + struct cx23885_dev *dev = port->dev; + u8 i, temp, ret = 0; + + temp = buf[0]; + for (i = 0; i < buf[0]; i++) + temp += buf[i + 1]; + temp = (~temp + 1);/* get check sum */ + buf[1 + buf[0]] = temp; + + f300_set_line(dev, F300_RESET, 1); + f300_set_line(dev, F300_CLK, 1); + udelay(30); + f300_set_line(dev, F300_DATA, 1); + msleep(1); + + /* question: */ + f300_set_line(dev, F300_RESET, 0);/* begin to send data */ + msleep(1); + + f300_send_byte(dev, 0xe0);/* the slave address is 0xe0, write */ + msleep(1); + + temp = buf[0]; + temp += 2; + for (i = 0; i < temp; i++) + f300_send_byte(dev, buf[i]); + + f300_set_line(dev, F300_RESET, 1);/* sent data over */ + f300_set_line(dev, F300_DATA, 1); + + /* answer: */ + temp = 0; + for (i = 0; ((i < 8) & (temp == 0)); i++) { + msleep(1); + if (f300_get_line(dev, F300_BUSY) == 0) + temp = 1; + } + + if (i > 7) { + printk(KERN_ERR "%s: timeout, the slave no response\n", + __func__); + ret = 1; /* timeout, the slave no response */ + } else { /* the slave not busy, prepare for getting data */ + f300_set_line(dev, F300_RESET, 0);/*ready...*/ + msleep(1); + f300_send_byte(dev, 0xe1);/* 0xe1 is Read */ + msleep(1); + temp = f300_get_byte(dev);/*get the data length */ + if (temp > 14) + temp = 14; + + for (i = 0; i < (temp + 1); i++) + f300_get_byte(dev);/* get data to empty buffer */ + + f300_set_line(dev, F300_RESET, 1);/* received data over */ + f300_set_line(dev, F300_DATA, 1); + } + + return ret; +} + +int f300_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage) +{ + u8 buf[16]; + + buf[0] = 0x05; + buf[1] = 0x38;/* write port */ + buf[2] = 0x01;/* A port, lnb power */ + + switch (voltage) { + case SEC_VOLTAGE_13: + buf[3] = 0x01;/* power on */ + buf[4] = 0x02;/* B port, H/V */ + buf[5] = 0x00;/*13V v*/ + break; + case SEC_VOLTAGE_18: + buf[3] = 0x01; + buf[4] = 0x02; + buf[5] = 0x01;/* 18V h*/ + break; + case SEC_VOLTAGE_OFF: + buf[3] = 0x00;/* power off */ + buf[4] = 0x00; + buf[5] = 0x00; + break; + } + + return f300_xfer(fe, buf); +} diff --git a/drivers/media/video/cx23885/cx23885-f300.h b/drivers/media/video/cx23885/cx23885-f300.h new file mode 100644 index 0000000000000..e73344c949638 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-f300.h @@ -0,0 +1,2 @@ +extern int f300_set_voltage(struct dvb_frontend *fe, + fe_sec_voltage_t voltage); diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c new file mode 100644 index 0000000000000..469e083dd5f89 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -0,0 +1,427 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared remote control input device + * + * Most of this file is + * + * Copyright (C) 2009 Andy Walls + * + * However, the cx23885_input_{init,fini} functions contained herein are + * derived from Linux kernel files linux/media/video/.../...-input.c marked as: + * + * Copyright (C) 2008 + * Copyright (C) 2005 Ludovico Cavedon + * Markus Rechberger + * Mauro Carvalho Chehab + * Sascha Sommer + * Copyright (C) 2004, 2005 Chris Pascoe + * Copyright (C) 2003, 2004 Gerd Knorr + * Copyright (C) 2003 Pavel Machek + * + * 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, or (at your option) any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include +#include +#include + +#include "cx23885.h" + +#define RC5_BITS 14 +#define RC5_HALF_BITS (2*RC5_BITS) +#define RC5_HALF_BITS_MASK ((1 << RC5_HALF_BITS) - 1) + +#define RC5_START_BITS_NORMAL 0x3 /* Command range 0 - 63 */ +#define RC5_START_BITS_EXTENDED 0x2 /* Command range 64 - 127 */ + +#define RC5_EXTENDED_COMMAND_OFFSET 64 + +static inline unsigned int rc5_command(u32 rc5_baseband) +{ + return RC5_INSTR(rc5_baseband) + + ((RC5_START(rc5_baseband) == RC5_START_BITS_EXTENDED) + ? RC5_EXTENDED_COMMAND_OFFSET : 0); +} + +static void cx23885_input_process_raw_rc5(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + unsigned int code, command; + u32 rc5; + + /* Ignore codes that are too short to be valid RC-5 */ + if (ir_input->last_bit < (RC5_HALF_BITS - 1)) + return; + + /* The library has the manchester coding backwards; XOR to adapt. */ + code = (ir_input->code & RC5_HALF_BITS_MASK) ^ RC5_HALF_BITS_MASK; + rc5 = ir_rc5_decode(code); + + switch (RC5_START(rc5)) { + case RC5_START_BITS_NORMAL: + break; + case RC5_START_BITS_EXTENDED: + /* Don't allow if the remote only emits standard commands */ + if (ir_input->start == RC5_START_BITS_NORMAL) + return; + break; + default: + return; + } + + if (ir_input->addr != RC5_ADDR(rc5)) + return; + + /* Don't generate a keypress for RC-5 auto-repeated keypresses */ + command = rc5_command(rc5); + if (RC5_TOGGLE(rc5) != RC5_TOGGLE(ir_input->last_rc5) || + command != rc5_command(ir_input->last_rc5) || + /* Catch T == 0, CMD == 0 (e.g. '0') as first keypress after init */ + RC5_START(ir_input->last_rc5) == 0) { + /* This keypress is differnet: not an auto repeat */ + ir_input_nokey(ir_input->dev, &ir_input->ir); + ir_input_keydown(ir_input->dev, &ir_input->ir, command); + } + ir_input->last_rc5 = rc5; + + /* Schedule when we should do the key up event: ir_input_nokey() */ + mod_timer(&ir_input->timer_keyup, + jiffies + msecs_to_jiffies(ir_input->rc5_key_timeout)); +} + +static void cx23885_input_next_pulse_width_rc5(struct cx23885_dev *dev, + u32 ns_pulse) +{ + const int rc5_quarterbit_ns = 444444; /* 32 cycles/36 kHz/2 = 444 us */ + struct card_ir *ir_input = dev->ir_input; + int i, level, quarterbits, halfbits; + + if (!ir_input->active) { + ir_input->active = 1; + /* assume an initial space that we may not detect or measure */ + ir_input->code = 0; + ir_input->last_bit = 0; + } + + if (ns_pulse == V4L2_SUBDEV_IR_PULSE_RX_SEQ_END) { + ir_input->last_bit++; /* Account for the final space */ + ir_input->active = 0; + cx23885_input_process_raw_rc5(dev); + return; + } + + level = (ns_pulse & V4L2_SUBDEV_IR_PULSE_LEVEL_MASK) ? 1 : 0; + + /* Skip any leading space to sync to the start bit */ + if (ir_input->last_bit == 0 && level == 0) + return; + + /* + * With valid RC-5 we can get up to two consecutive half-bits in a + * single pulse measurment. Experiments have shown that the duration + * of a half-bit can vary. Make sure we always end up with an even + * number of quarter bits at the same level (mark or space). + */ + ns_pulse &= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + quarterbits = ns_pulse / rc5_quarterbit_ns; + if (quarterbits & 1) + quarterbits++; + halfbits = quarterbits / 2; + + for (i = 0; i < halfbits; i++) { + ir_input->last_bit++; + ir_input->code |= (level << ir_input->last_bit); + + if (ir_input->last_bit >= RC5_HALF_BITS-1) { + ir_input->active = 0; + cx23885_input_process_raw_rc5(dev); + /* + * If level is 1, a leading mark is invalid for RC5. + * If level is 0, we scan past extra intial space. + * Either way we don't want to reactivate collecting + * marks or spaces here with any left over half-bits. + */ + break; + } + } +} + +static void cx23885_input_process_pulse_widths_rc5(struct cx23885_dev *dev, + bool add_eom) +{ + struct card_ir *ir_input = dev->ir_input; + struct ir_input_state *ir_input_state = &ir_input->ir; + + u32 ns_pulse[RC5_HALF_BITS+1]; + ssize_t num = 0; + int count, i; + + do { + v4l2_subdev_call(dev->sd_ir, ir, rx_read, (u8 *) ns_pulse, + sizeof(ns_pulse), &num); + + count = num / sizeof(u32); + + /* Append an end of Rx seq, if the caller requested */ + if (add_eom && count < ARRAY_SIZE(ns_pulse)) { + ns_pulse[count] = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; + count++; + } + + /* Just drain the Rx FIFO, if we're called, but not RC-5 */ + if (ir_input_state->ir_type != IR_TYPE_RC5) + continue; + + for (i = 0; i < count; i++) + cx23885_input_next_pulse_width_rc5(dev, ns_pulse[i]); + } while (num != 0); +} + +void cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events) +{ + struct v4l2_subdev_ir_parameters params; + int overrun, data_available; + + if (dev->sd_ir == NULL || events == 0) + return; + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* + * The only board we handle right now. However other boards + * using the CX2388x integrated IR controller should be similar + */ + break; + default: + return; + } + + overrun = events & (V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN | + V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN); + + data_available = events & (V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED | + V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ); + + if (overrun) { + /* If there was a FIFO overrun, stop the device */ + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + params.enable = false; + /* Mitigate race with cx23885_input_ir_stop() */ + params.shutdown = atomic_read(&dev->ir_input_stopping); + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + } + + if (data_available) + cx23885_input_process_pulse_widths_rc5(dev, overrun); + + if (overrun) { + /* If there was a FIFO overrun, clear & restart the device */ + params.enable = true; + /* Mitigate race with cx23885_input_ir_stop() */ + params.shutdown = atomic_read(&dev->ir_input_stopping); + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + } +} + +static void cx23885_input_ir_start(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + struct ir_input_state *ir_input_state = &ir_input->ir; + struct v4l2_subdev_ir_parameters params; + + if (dev->sd_ir == NULL) + return; + + atomic_set(&dev->ir_input_stopping, 0); + + /* keyup timer set up, if needed */ + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + setup_timer(&ir_input->timer_keyup, + ir_rc5_timer_keyup, /* Not actually RC-5 specific */ + (unsigned long) ir_input); + if (ir_input_state->ir_type == IR_TYPE_RC5) { + /* + * RC-5 repeats a held key every + * 64 bits * (2 * 32/36000) sec/bit = 113.778 ms + */ + ir_input->rc5_key_timeout = 115; + } + break; + } + + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* + * The IR controller on this board only returns pulse widths. + * Any other mode setting will fail to set up the device. + */ + params.mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + params.enable = true; + params.interrupt_enable = true; + params.shutdown = false; + + /* Setup for baseband compatible with both RC-5 and RC-6A */ + params.modulation = false; + /* RC-5: 2,222,222 ns = 1/36 kHz * 32 cycles * 2 marks * 1.25*/ + /* RC-6A: 3,333,333 ns = 1/36 kHz * 16 cycles * 6 marks * 1.25*/ + params.max_pulse_width = 3333333; /* ns */ + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + params.noise_filter_min_width = 333333; /* ns */ + /* + * This board has inverted receive sense: + * mark is received as low logic level; + * falling edges are detected as rising edges; etc. + */ + params.invert = true; + break; + } + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); +} + +static void cx23885_input_ir_stop(struct cx23885_dev *dev) +{ + struct card_ir *ir_input = dev->ir_input; + struct v4l2_subdev_ir_parameters params; + + if (dev->sd_ir == NULL) + return; + + /* + * Stop the sd_ir subdevice from generating notifications and + * scheduling work. + * It is shutdown this way in order to mitigate a race with + * cx23885_input_rx_work_handler() in the overrun case, which could + * re-enable the subdevice. + */ + atomic_set(&dev->ir_input_stopping, 1); + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + while (params.shutdown == false) { + params.enable = false; + params.interrupt_enable = false; + params.shutdown = true; + v4l2_subdev_call(dev->sd_ir, ir, rx_s_parameters, ¶ms); + v4l2_subdev_call(dev->sd_ir, ir, rx_g_parameters, ¶ms); + } + + flush_scheduled_work(); + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + del_timer_sync(&ir_input->timer_keyup); + break; + } +} + +int cx23885_input_init(struct cx23885_dev *dev) +{ + struct card_ir *ir; + struct input_dev *input_dev; + struct ir_scancode_table *ir_codes = NULL; + int ir_type, ir_addr, ir_start; + int ret; + + /* + * If the IR device (hardware registers, chip, GPIO lines, etc.) isn't + * encapsulated in a v4l2_subdev, then I'm not going to deal with it. + */ + if (dev->sd_ir == NULL) + return -ENODEV; + + switch (dev->board) { + case CX23885_BOARD_HAUPPAUGE_HVR1850: + case CX23885_BOARD_HAUPPAUGE_HVR1290: + /* Parameters for the grey Hauppauge remote for the HVR-1850 */ + ir_codes = &ir_codes_hauppauge_new_table; + ir_type = IR_TYPE_RC5; + ir_addr = 0x1e; /* RC-5 system bits emitted by the remote */ + ir_start = RC5_START_BITS_NORMAL; /* A basic RC-5 remote */ + break; + } + if (ir_codes == NULL) + return -ENODEV; + + ir = kzalloc(sizeof(*ir), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!ir || !input_dev) { + ret = -ENOMEM; + goto err_out_free; + } + + ir->dev = input_dev; + ir->addr = ir_addr; + ir->start = ir_start; + + /* init input device */ + snprintf(ir->name, sizeof(ir->name), "cx23885 IR (%s)", + cx23885_boards[dev->board].name); + snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); + + ret = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (ret < 0) + goto err_out_free; + + input_dev->name = ir->name; + input_dev->phys = ir->phys; + input_dev->id.bustype = BUS_PCI; + input_dev->id.version = 1; + if (dev->pci->subsystem_vendor) { + input_dev->id.vendor = dev->pci->subsystem_vendor; + input_dev->id.product = dev->pci->subsystem_device; + } else { + input_dev->id.vendor = dev->pci->vendor; + input_dev->id.product = dev->pci->device; + } + input_dev->dev.parent = &dev->pci->dev; + + dev->ir_input = ir; + cx23885_input_ir_start(dev); + + ret = input_register_device(ir->dev); + if (ret) + goto err_out_stop; + + return 0; + +err_out_stop: + cx23885_input_ir_stop(dev); + dev->ir_input = NULL; +err_out_free: + ir_input_free(input_dev); + input_free_device(input_dev); + kfree(ir); + return ret; +} + +void cx23885_input_fini(struct cx23885_dev *dev) +{ + /* Always stop the IR hardware from generating interrupts */ + cx23885_input_ir_stop(dev); + + if (dev->ir_input == NULL) + return; + ir_input_free(dev->ir_input->dev); + input_unregister_device(dev->ir_input->dev); + kfree(dev->ir_input); + dev->ir_input = NULL; +} diff --git a/drivers/media/video/cx23885/cx23885-input.h b/drivers/media/video/cx23885/cx23885-input.h new file mode 100644 index 0000000000000..3572cb1ecfc2f --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-input.h @@ -0,0 +1,30 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared remote control input device + * + * Copyright (C) 2009 Andy Walls + * + * 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, or (at your option) any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23885_INPUT_H_ +#define _CX23885_INPUT_H_ +int cx23885_input_rx_work_handler(struct cx23885_dev *dev, u32 events); + +int cx23885_input_init(struct cx23885_dev *dev); +void cx23885_input_fini(struct cx23885_dev *dev); +#endif diff --git a/drivers/media/video/cx23885/cx23885-ioctl.c b/drivers/media/video/cx23885/cx23885-ioctl.c new file mode 100644 index 0000000000000..dfb4627fb3402 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ioctl.c @@ -0,0 +1,208 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Various common ioctl() support functions + * + * Copyright (c) 2009 Andy Walls + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx23885.h" +#include + +int cx23885_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + int err = 0; + u8 rev; + + chip->ident = V4L2_IDENT_NONE; + chip->revision = 0; + switch (chip->match.type) { + case V4L2_CHIP_MATCH_HOST: + switch (chip->match.addr) { + case 0: + rev = cx_read(RDR_CFG2) & 0xff; + switch (dev->pci->device) { + case 0x8852: + /* rev 0x04 could be '885 or '888. Pick '888. */ + if (rev == 0x04) + chip->ident = V4L2_IDENT_CX23888; + else + chip->ident = V4L2_IDENT_CX23885; + break; + case 0x8880: + if (rev == 0x0e || rev == 0x0f) + chip->ident = V4L2_IDENT_CX23887; + else + chip->ident = V4L2_IDENT_CX23888; + break; + default: + chip->ident = V4L2_IDENT_UNKNOWN; + break; + } + chip->revision = (dev->pci->device << 16) | (rev << 8) | + (dev->hwrevision & 0xff); + break; + case 1: + if (dev->v4l_device != NULL) { + chip->ident = V4L2_IDENT_CX23417; + chip->revision = 0; + } + break; + case 2: + /* + * The integrated IR controller on the CX23888 is + * host chip 2. It may not be used/initialized or sd_ir + * may be pointing at the cx25840 subdevice for the + * IR controller on the CX23885. Thus we find it + * without using the dev->sd_ir pointer. + */ + call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident, + chip); + break; + default: + err = -EINVAL; /* per V4L2 spec */ + break; + } + break; + case V4L2_CHIP_MATCH_I2C_DRIVER: + /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */ + call_all(dev, core, g_chip_ident, chip); + break; + case V4L2_CHIP_MATCH_I2C_ADDR: + /* + * We could return V4L2_IDENT_UNKNOWN, but we don't do the work + * to look if a chip is at the address with no driver. That's a + * dangerous thing to do with EEPROMs anyway. + */ + call_all(dev, core, g_chip_ident, chip); + break; + default: + err = -EINVAL; + break; + } + return err; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx23885_g_host_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) + return -EINVAL; + + reg->size = 4; + reg->val = cx_read(reg->reg); + return 0; +} + +static int cx23417_g_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + u32 value; + + if (dev->v4l_device == NULL) + return -EINVAL; + + if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000) + return -EINVAL; + + if (mc417_register_read(dev, (u16) reg->reg, &value)) + return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ + + reg->size = 4; + reg->val = value; + return 0; +} + +int cx23885_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (reg->match.type == V4L2_CHIP_MATCH_HOST) { + switch (reg->match.addr) { + case 0: + return cx23885_g_host_register(dev, reg); + case 1: + return cx23417_g_register(dev, reg); + default: + break; + } + } + + /* FIXME - any error returns should not be ignored */ + call_all(dev, core, g_register, reg); + return 0; +} + +static int cx23885_s_host_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0)) + return -EINVAL; + + reg->size = 4; + cx_write(reg->reg, reg->val); + return 0; +} + +static int cx23417_s_register(struct cx23885_dev *dev, + struct v4l2_dbg_register *reg) +{ + if (dev->v4l_device == NULL) + return -EINVAL; + + if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000) + return -EINVAL; + + if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val)) + return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */ + + reg->size = 4; + return 0; +} + +int cx23885_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (reg->match.type == V4L2_CHIP_MATCH_HOST) { + switch (reg->match.addr) { + case 0: + return cx23885_s_host_register(dev, reg); + case 1: + return cx23417_s_register(dev, reg); + default: + break; + } + } + + /* FIXME - any error returns should not be ignored */ + call_all(dev, core, s_register, reg); + return 0; +} +#endif diff --git a/drivers/media/video/cx23885/cx23885-ioctl.h b/drivers/media/video/cx23885/cx23885-ioctl.h new file mode 100644 index 0000000000000..80b0f4923c6a7 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ioctl.h @@ -0,0 +1,39 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Various common ioctl() support functions + * + * Copyright (c) 2009 Andy Walls + * + * 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, or + * (at your option) any later version. + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _CX23885_IOCTL_H_ +#define _CX23885_IOCTL_H_ + +int cx23885_g_chip_ident(struct file *file, void *fh, + struct v4l2_dbg_chip_ident *chip); + +#ifdef CONFIG_VIDEO_ADV_DEBUG +int cx23885_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); + + +int cx23885_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); + +#endif +#endif diff --git a/drivers/media/video/cx23885/cx23885-ir.c b/drivers/media/video/cx23885/cx23885-ir.c new file mode 100644 index 0000000000000..6ae982cc98562 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ir.c @@ -0,0 +1,101 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared device support routines - non-input, non-vl42_subdev routines + * + * Copyright (C) 2009 Andy Walls + * + * 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, or (at your option) any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include + +#include "cx23885.h" +#include "cx23885-input.h" + +#define CX23885_IR_RX_FIFO_SERVICE_REQ 0 +#define CX23885_IR_RX_END_OF_RX_DETECTED 1 +#define CX23885_IR_RX_HW_FIFO_OVERRUN 2 +#define CX23885_IR_RX_SW_FIFO_OVERRUN 3 + +#define CX23885_IR_TX_FIFO_SERVICE_REQ 0 + + +void cx23885_ir_rx_work_handler(struct work_struct *work) +{ + struct cx23885_dev *dev = + container_of(work, struct cx23885_dev, ir_rx_work); + u32 events = 0; + unsigned long *notifications = &dev->ir_rx_notifications; + + if (test_and_clear_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications)) + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + if (test_and_clear_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications)) + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + if (test_and_clear_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications)) + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + if (test_and_clear_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications)) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + + if (events == 0) + return; + + if (dev->ir_input) + cx23885_input_rx_work_handler(dev, events); +} + +void cx23885_ir_tx_work_handler(struct work_struct *work) +{ + struct cx23885_dev *dev = + container_of(work, struct cx23885_dev, ir_tx_work); + u32 events = 0; + unsigned long *notifications = &dev->ir_tx_notifications; + + if (test_and_clear_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications)) + events |= V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + + if (events == 0) + return; + +} + +/* Called in an IRQ context */ +void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) +{ + struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); + unsigned long *notifications = &dev->ir_rx_notifications; + + if (events & V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ) + set_bit(CX23885_IR_RX_FIFO_SERVICE_REQ, notifications); + if (events & V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED) + set_bit(CX23885_IR_RX_END_OF_RX_DETECTED, notifications); + if (events & V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN) + set_bit(CX23885_IR_RX_HW_FIFO_OVERRUN, notifications); + if (events & V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN) + set_bit(CX23885_IR_RX_SW_FIFO_OVERRUN, notifications); + schedule_work(&dev->ir_rx_work); +} + +/* Called in an IRQ context */ +void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events) +{ + struct cx23885_dev *dev = to_cx23885(sd->v4l2_dev); + unsigned long *notifications = &dev->ir_tx_notifications; + + if (events & V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ) + set_bit(CX23885_IR_TX_FIFO_SERVICE_REQ, notifications); + schedule_work(&dev->ir_tx_work); +} diff --git a/drivers/media/video/cx23885/cx23885-ir.h b/drivers/media/video/cx23885/cx23885-ir.h new file mode 100644 index 0000000000000..9b8a6d5d1ef63 --- /dev/null +++ b/drivers/media/video/cx23885/cx23885-ir.h @@ -0,0 +1,31 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * Infrared device support routines - non-input, non-vl42_subdev routines + * + * Copyright (C) 2009 Andy Walls + * + * 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, or (at your option) any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23885_IR_H_ +#define _CX23885_IR_H_ +void cx23885_ir_rx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events); +void cx23885_ir_tx_v4l2_dev_notify(struct v4l2_subdev *sd, u32 events); + +void cx23885_ir_rx_work_handler(struct work_struct *work); +void cx23885_ir_tx_work_handler(struct work_struct *work); +#endif diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h index eafbe5226bae4..c0bc9a068954b 100644 --- a/drivers/media/video/cx23885/cx23885-reg.h +++ b/drivers/media/video/cx23885/cx23885-reg.h @@ -212,8 +212,9 @@ Channel manager Data Structure entry = 20 DWORD #define DEV_CNTRL2 0x00040000 -#define PCI_MSK_GPIO1 (1 << 24) -#define PCI_MSK_GPIO0 (1 << 23) +#define PCI_MSK_IR (1 << 28) +#define PCI_MSK_GPIO1 (1 << 24) +#define PCI_MSK_GPIO0 (1 << 23) #define PCI_MSK_APB_DMA (1 << 12) #define PCI_MSK_AL_WR (1 << 11) #define PCI_MSK_AL_RD (1 << 10) diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c index 654cc253cd509..8b372b4f0de25 100644 --- a/drivers/media/video/cx23885/cx23885-video.c +++ b/drivers/media/video/cx23885/cx23885-video.c @@ -35,6 +35,7 @@ #include "cx23885.h" #include #include +#include "cx23885-ioctl.h" MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards"); MODULE_AUTHOR("Steven Toth "); @@ -401,6 +402,13 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int input) INPUT(input)->gpio2, INPUT(input)->gpio3); dev->input = input; + if (dev->board == CX23885_BOARD_MYGICA_X8506 || + dev->board == CX23885_BOARD_MAGICPRO_PROHDTVE2) { + /* Select Analog TV */ + if (INPUT(input)->type == CX23885_VMUX_TELEVISION) + cx23885_gpio_clear(dev, GPIO_0); + } + /* Tell the internal A/V decoder */ v4l2_subdev_call(dev->sd_cx25840, video, s_routing, INPUT(input)->vmux, 0, 0); @@ -1144,6 +1152,7 @@ static int cx23885_enum_input(struct cx23885_dev *dev, struct v4l2_input *i) [CX23885_VMUX_COMPOSITE3] = "Composite3", [CX23885_VMUX_COMPOSITE4] = "Composite4", [CX23885_VMUX_SVIDEO] = "S-Video", + [CX23885_VMUX_COMPONENT] = "Component", [CX23885_VMUX_TELEVISION] = "Television", [CX23885_VMUX_CABLE] = "Cable TV", [CX23885_VMUX_DVB] = "DVB", @@ -1312,34 +1321,6 @@ static int vidioc_s_frequency(struct file *file, void *priv, cx23885_set_freq(dev, f); } -#ifdef CONFIG_VIDEO_ADV_DEBUG -static int vidioc_g_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - - call_all(dev, core, g_register, reg); - - return 0; -} - -static int vidioc_s_register(struct file *file, void *fh, - struct v4l2_dbg_register *reg) -{ - struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev; - - if (!v4l2_chip_match_host(®->match)) - return -EINVAL; - - call_all(dev, core, s_register, reg); - - return 0; -} -#endif - /* ----------------------------------------------------------- */ static void cx23885_vid_timeout(unsigned long data) @@ -1449,9 +1430,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_g_chip_ident = cx23885_g_chip_ident, #ifdef CONFIG_VIDEO_ADV_DEBUG - .vidioc_g_register = vidioc_g_register, - .vidioc_s_register = vidioc_s_register, + .vidioc_g_register = cx23885_g_register, + .vidioc_s_register = cx23885_s_register, #endif }; @@ -1529,9 +1511,11 @@ int cx23885_video_register(struct cx23885_dev *dev) if (sd) { struct tuner_setup tun_setup; + memset(&tun_setup, 0, sizeof(tun_setup)); tun_setup.mode_mask = T_ANALOG_TV; tun_setup.type = dev->tuner_type; tun_setup.addr = v4l2_i2c_subdev_addr(sd); + tun_setup.tuner_callback = cx23885_tuner_callback; v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup); } diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index cc7a165561ff5..fa744764dc8bd 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -79,6 +79,8 @@ #define CX23885_BOARD_MAGICPRO_PROHDTVE2 23 #define CX23885_BOARD_HAUPPAUGE_HVR1850 24 #define CX23885_BOARD_COMPRO_VIDEOMATE_E800 25 +#define CX23885_BOARD_HAUPPAUGE_HVR1290 26 +#define CX23885_BOARD_MYGICA_X8558PRO 27 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 @@ -157,6 +159,7 @@ enum cx23885_itype { CX23885_VMUX_COMPOSITE3, CX23885_VMUX_COMPOSITE4, CX23885_VMUX_SVIDEO, + CX23885_VMUX_COMPONENT, CX23885_VMUX_TELEVISION, CX23885_VMUX_CABLE, CX23885_VMUX_DVB, @@ -297,10 +300,6 @@ struct cx23885_tsport { /* Allow a single tsport to have multiple frontends */ u32 num_frontends; void *port_priv; - - /* FIXME: temporary hack */ - int (*set_frontend_save) (struct dvb_frontend *, - struct dvb_frontend_parameters *); }; struct cx23885_dev { @@ -356,6 +355,16 @@ struct cx23885_dev { unsigned int has_radio; struct v4l2_subdev *sd_cx25840; + /* Infrared */ + struct v4l2_subdev *sd_ir; + struct work_struct ir_rx_work; + unsigned long ir_rx_notifications; + struct work_struct ir_tx_work; + unsigned long ir_tx_notifications; + + struct card_ir *ir_input; + atomic_t ir_input_stopping; + /* V4l */ u32 freq; struct video_device *video_dev; @@ -383,6 +392,13 @@ static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev) #define call_all(dev, o, f, args...) \ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) +#define CX23885_HW_888_IR (1 << 0) + +#define call_hw(dev, grpid, o, f, args...) \ + v4l2_device_call_all(&dev->v4l2_dev, grpid, o, f, ##args) + +extern struct v4l2_subdev *cx23885_find_hw(struct cx23885_dev *dev, u32 hw); + extern struct list_head cx23885_devlist; #define SRAM_CH01 0 /* Video A */ @@ -455,6 +471,7 @@ extern void cx23885_wakeup(struct cx23885_tsport *port, extern void cx23885_gpio_set(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_clear(struct cx23885_dev *dev, u32 mask); +extern u32 cx23885_gpio_get(struct cx23885_dev *dev, u32 mask); extern void cx23885_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); @@ -471,6 +488,8 @@ extern int cx23885_tuner_callback(void *priv, int component, int command, int arg); extern void cx23885_card_list(struct cx23885_dev *dev); extern int cx23885_ir_init(struct cx23885_dev *dev); +extern void cx23885_ir_pci_int_enable(struct cx23885_dev *dev); +extern void cx23885_ir_fini(struct cx23885_dev *dev); extern void cx23885_gpio_setup(struct cx23885_dev *dev); extern void cx23885_card_setup(struct cx23885_dev *dev); extern void cx23885_card_setup_pre_i2c(struct cx23885_dev *dev); @@ -515,6 +534,10 @@ extern void cx23885_417_check_encoder(struct cx23885_dev *dev); extern void cx23885_mc417_init(struct cx23885_dev *dev); extern int mc417_memory_read(struct cx23885_dev *dev, u32 address, u32 *value); extern int mc417_memory_write(struct cx23885_dev *dev, u32 address, u32 value); +extern int mc417_register_read(struct cx23885_dev *dev, + u16 address, u32 *value); +extern int mc417_register_write(struct cx23885_dev *dev, + u16 address, u32 value); extern void mc417_gpio_set(struct cx23885_dev *dev, u32 mask); extern void mc417_gpio_clear(struct cx23885_dev *dev, u32 mask); extern void mc417_gpio_enable(struct cx23885_dev *dev, u32 mask, int asoutput); diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c new file mode 100644 index 0000000000000..3ccc8afeccf32 --- /dev/null +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -0,0 +1,1239 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * CX23888 Integrated Consumer Infrared Controller + * + * Copyright (C) 2009 Andy Walls + * + * 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, or (at your option) any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include + +#include +#include + +#include "cx23885.h" + +static unsigned int ir_888_debug; +module_param(ir_888_debug, int, 0644); +MODULE_PARM_DESC(ir_888_debug, "enable debug messages [CX23888 IR controller]"); + +#define CX23888_IR_REG_BASE 0x170000 +/* + * These CX23888 register offsets have a straightforward one to one mapping + * to the CX23885 register offsets of 0x200 through 0x218 + */ +#define CX23888_IR_CNTRL_REG 0x170000 +#define CNTRL_WIN_3_3 0x00000000 +#define CNTRL_WIN_4_3 0x00000001 +#define CNTRL_WIN_3_4 0x00000002 +#define CNTRL_WIN_4_4 0x00000003 +#define CNTRL_WIN 0x00000003 +#define CNTRL_EDG_NONE 0x00000000 +#define CNTRL_EDG_FALL 0x00000004 +#define CNTRL_EDG_RISE 0x00000008 +#define CNTRL_EDG_BOTH 0x0000000C +#define CNTRL_EDG 0x0000000C +#define CNTRL_DMD 0x00000010 +#define CNTRL_MOD 0x00000020 +#define CNTRL_RFE 0x00000040 +#define CNTRL_TFE 0x00000080 +#define CNTRL_RXE 0x00000100 +#define CNTRL_TXE 0x00000200 +#define CNTRL_RIC 0x00000400 +#define CNTRL_TIC 0x00000800 +#define CNTRL_CPL 0x00001000 +#define CNTRL_LBM 0x00002000 +#define CNTRL_R 0x00004000 + +#define CX23888_IR_TXCLK_REG 0x170004 +#define TXCLK_TCD 0x0000FFFF + +#define CX23888_IR_RXCLK_REG 0x170008 +#define RXCLK_RCD 0x0000FFFF + +#define CX23888_IR_CDUTY_REG 0x17000C +#define CDUTY_CDC 0x0000000F + +#define CX23888_IR_STATS_REG 0x170010 +#define STATS_RTO 0x00000001 +#define STATS_ROR 0x00000002 +#define STATS_RBY 0x00000004 +#define STATS_TBY 0x00000008 +#define STATS_RSR 0x00000010 +#define STATS_TSR 0x00000020 + +#define CX23888_IR_IRQEN_REG 0x170014 +#define IRQEN_RTE 0x00000001 +#define IRQEN_ROE 0x00000002 +#define IRQEN_RSE 0x00000010 +#define IRQEN_TSE 0x00000020 + +#define CX23888_IR_FILTR_REG 0x170018 +#define FILTR_LPF 0x0000FFFF + +/* This register doesn't follow the pattern; it's 0x23C on a CX23885 */ +#define CX23888_IR_FIFO_REG 0x170040 +#define FIFO_RXTX 0x0000FFFF +#define FIFO_RXTX_LVL 0x00010000 +#define FIFO_RXTX_RTO 0x0001FFFF +#define FIFO_RX_NDV 0x00020000 +#define FIFO_RX_DEPTH 8 +#define FIFO_TX_DEPTH 8 + +/* CX23888 unique registers */ +#define CX23888_IR_SEEDP_REG 0x17001C +#define CX23888_IR_TIMOL_REG 0x170020 +#define CX23888_IR_WAKE0_REG 0x170024 +#define CX23888_IR_WAKE1_REG 0x170028 +#define CX23888_IR_WAKE2_REG 0x17002C +#define CX23888_IR_MASK0_REG 0x170030 +#define CX23888_IR_MASK1_REG 0x170034 +#define CX23888_IR_MAKS2_REG 0x170038 +#define CX23888_IR_DPIPG_REG 0x17003C +#define CX23888_IR_LEARN_REG 0x170044 + +#define CX23888_VIDCLK_FREQ 108000000 /* 108 MHz, BT.656 */ +#define CX23888_IR_REFCLK_FREQ (CX23888_VIDCLK_FREQ / 2) + +#define CX23888_IR_RX_KFIFO_SIZE (512 * sizeof(u32)) +#define CX23888_IR_TX_KFIFO_SIZE (512 * sizeof(u32)) + +struct cx23888_ir_state { + struct v4l2_subdev sd; + struct cx23885_dev *dev; + u32 id; + u32 rev; + + struct v4l2_subdev_ir_parameters rx_params; + struct mutex rx_params_lock; + atomic_t rxclk_divider; + atomic_t rx_invert; + + struct kfifo *rx_kfifo; + spinlock_t rx_kfifo_lock; + + struct v4l2_subdev_ir_parameters tx_params; + struct mutex tx_params_lock; + atomic_t txclk_divider; + + struct kfifo *tx_kfifo; + spinlock_t tx_kfifo_lock; +}; + +static inline struct cx23888_ir_state *to_state(struct v4l2_subdev *sd) +{ + return v4l2_get_subdevdata(sd); +} + +/* + * IR register block read and write functions + */ +static +inline int cx23888_ir_write4(struct cx23885_dev *dev, u32 addr, u32 value) +{ + cx_write(addr, value); + return 0; +} + +static inline u32 cx23888_ir_read4(struct cx23885_dev *dev, u32 addr) +{ + return cx_read(addr); +} + +static inline int cx23888_ir_and_or4(struct cx23885_dev *dev, u32 addr, + u32 and_mask, u32 or_value) +{ + cx_andor(addr, ~and_mask, or_value); + return 0; +} + +/* + * Rx and Tx Clock Divider register computations + * + * Note the largest clock divider value of 0xffff corresponds to: + * (0xffff + 1) * 1000 / 108/2 MHz = 1,213,629.629... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_clock_divider(unsigned int d) +{ + if (d > RXCLK_RCD + 1) + d = RXCLK_RCD; + else if (d < 2) + d = 1; + else + d--; + return (u16) d; +} + +static inline u16 ns_to_clock_divider(unsigned int ns) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int clock_divider_to_ns(unsigned int divider) +{ + /* Period of the Rx or Tx clock in ns */ + return DIV_ROUND_CLOSEST((divider + 1) * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static inline u16 carrier_freq_to_clock_divider(unsigned int freq) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * 16)); +} + +static inline unsigned int clock_divider_to_carrier_freq(unsigned int divider) +{ + return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, (divider + 1) * 16); +} + +static inline u16 freq_to_clock_divider(unsigned int freq, + unsigned int rollovers) +{ + return count_to_clock_divider( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, freq * rollovers)); +} + +static inline unsigned int clock_divider_to_freq(unsigned int divider, + unsigned int rollovers) +{ + return DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ, + (divider + 1) * rollovers); +} + +/* + * Low Pass Filter register calculations + * + * Note the largest count value of 0xffff corresponds to: + * 0xffff * 1000 / 108/2 MHz = 1,213,611.11... ns + * which fits in 21 bits, so we'll use unsigned int for time arguments. + */ +static inline u16 count_to_lpf_count(unsigned int d) +{ + if (d > FILTR_LPF) + d = FILTR_LPF; + else if (d < 4) + d = 0; + return (u16) d; +} + +static inline u16 ns_to_lpf_count(unsigned int ns) +{ + return count_to_lpf_count( + DIV_ROUND_CLOSEST(CX23888_IR_REFCLK_FREQ / 1000000 * ns, 1000)); +} + +static inline unsigned int lpf_count_to_ns(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in ns */ + return DIV_ROUND_CLOSEST(count * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static inline unsigned int lpf_count_to_us(unsigned int count) +{ + /* Duration of the Low Pass Filter rejection window in us */ + return DIV_ROUND_CLOSEST(count, CX23888_IR_REFCLK_FREQ / 1000000); +} + +/* + * FIFO register pulse width count compuations + */ +static u32 clock_divider_to_resolution(u16 divider) +{ + /* + * Resolution is the duration of 1 tick of the readable portion of + * of the pulse width counter as read from the FIFO. The two lsb's are + * not readable, hence the << 2. This function returns ns. + */ + return DIV_ROUND_CLOSEST((1 << 2) * ((u32) divider + 1) * 1000, + CX23888_IR_REFCLK_FREQ / 1000000); +} + +static u64 pulse_width_count_to_ns(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1) * 1000; /* millicycles */ + rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => ns */ + if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return n; +} + +static unsigned int pulse_width_count_to_us(u16 count, u16 divider) +{ + u64 n; + u32 rem; + + /* + * The 2 lsb's of the pulse width timer count are not readable, hence + * the (count << 2) | 0x3 + */ + n = (((u64) count << 2) | 0x3) * (divider + 1); /* cycles */ + rem = do_div(n, CX23888_IR_REFCLK_FREQ / 1000000); /* / MHz => us */ + if (rem >= CX23888_IR_REFCLK_FREQ / 1000000 / 2) + n++; + return (unsigned int) n; +} + +/* + * Pulse Clocks computations: Combined Pulse Width Count & Rx Clock Counts + * + * The total pulse clock count is an 18 bit pulse width timer count as the most + * significant part and (up to) 16 bit clock divider count as a modulus. + * When the Rx clock divider ticks down to 0, it increments the 18 bit pulse + * width timer count's least significant bit. + */ +static u64 ns_to_pulse_clocks(u32 ns) +{ + u64 clocks; + u32 rem; + clocks = CX23888_IR_REFCLK_FREQ / 1000000 * (u64) ns; /* millicycles */ + rem = do_div(clocks, 1000); /* /1000 = cycles */ + if (rem >= 1000 / 2) + clocks++; + return clocks; +} + +static u16 pulse_clocks_to_clock_divider(u64 count) +{ + u32 rem; + + rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + + /* net result needs to be rounded down and decremented by 1 */ + if (count > RXCLK_RCD + 1) + count = RXCLK_RCD; + else if (count < 2) + count = 1; + else + count--; + return (u16) count; +} + +/* + * IR Control Register helpers + */ +enum tx_fifo_watermark { + TX_FIFO_HALF_EMPTY = 0, + TX_FIFO_EMPTY = CNTRL_TIC, +}; + +enum rx_fifo_watermark { + RX_FIFO_HALF_FULL = 0, + RX_FIFO_NOT_EMPTY = CNTRL_RIC, +}; + +static inline void control_tx_irq_watermark(struct cx23885_dev *dev, + enum tx_fifo_watermark level) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_TIC, level); +} + +static inline void control_rx_irq_watermark(struct cx23885_dev *dev, + enum rx_fifo_watermark level) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_RIC, level); +} + +static inline void control_tx_enable(struct cx23885_dev *dev, bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_TXE | CNTRL_TFE), + enable ? (CNTRL_TXE | CNTRL_TFE) : 0); +} + +static inline void control_rx_enable(struct cx23885_dev *dev, bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~(CNTRL_RXE | CNTRL_RFE), + enable ? (CNTRL_RXE | CNTRL_RFE) : 0); +} + +static inline void control_tx_modulation_enable(struct cx23885_dev *dev, + bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_MOD, + enable ? CNTRL_MOD : 0); +} + +static inline void control_rx_demodulation_enable(struct cx23885_dev *dev, + bool enable) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_DMD, + enable ? CNTRL_DMD : 0); +} + +static inline void control_rx_s_edge_detection(struct cx23885_dev *dev, + u32 edge_types) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_EDG_BOTH, + edge_types & CNTRL_EDG_BOTH); +} + +static void control_rx_s_carrier_window(struct cx23885_dev *dev, + unsigned int carrier, + unsigned int *carrier_range_low, + unsigned int *carrier_range_high) +{ + u32 v; + unsigned int c16 = carrier * 16; + + if (*carrier_range_low < DIV_ROUND_CLOSEST(c16, 16 + 3)) { + v = CNTRL_WIN_3_4; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 4); + } else { + v = CNTRL_WIN_3_3; + *carrier_range_low = DIV_ROUND_CLOSEST(c16, 16 + 3); + } + + if (*carrier_range_high > DIV_ROUND_CLOSEST(c16, 16 - 3)) { + v |= CNTRL_WIN_4_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 4); + } else { + v |= CNTRL_WIN_3_3; + *carrier_range_high = DIV_ROUND_CLOSEST(c16, 16 - 3); + } + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_WIN, v); +} + +static inline void control_tx_polarity_invert(struct cx23885_dev *dev, + bool invert) +{ + cx23888_ir_and_or4(dev, CX23888_IR_CNTRL_REG, ~CNTRL_CPL, + invert ? CNTRL_CPL : 0); +} + +/* + * IR Rx & Tx Clock Register helpers + */ +static unsigned int txclk_tx_s_carrier(struct cx23885_dev *dev, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static unsigned int rxclk_rx_s_carrier(struct cx23885_dev *dev, + unsigned int freq, + u16 *divider) +{ + *divider = carrier_freq_to_clock_divider(freq); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); + return clock_divider_to_carrier_freq(*divider); +} + +static u32 txclk_tx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +static u32 rxclk_rx_s_max_pulse_width(struct cx23885_dev *dev, u32 ns, + u16 *divider) +{ + u64 pulse_clocks; + + if (ns > V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + ns = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS; + pulse_clocks = ns_to_pulse_clocks(ns); + *divider = pulse_clocks_to_clock_divider(pulse_clocks); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, *divider); + return (u32) pulse_width_count_to_ns(FIFO_RXTX, *divider); +} + +/* + * IR Tx Carrier Duty Cycle register helpers + */ +static unsigned int cduty_tx_s_duty_cycle(struct cx23885_dev *dev, + unsigned int duty_cycle) +{ + u32 n; + n = DIV_ROUND_CLOSEST(duty_cycle * 100, 625); /* 16ths of 100% */ + if (n != 0) + n--; + if (n > 15) + n = 15; + cx23888_ir_write4(dev, CX23888_IR_CDUTY_REG, n); + return DIV_ROUND_CLOSEST((n + 1) * 100, 16); +} + +/* + * IR Filter Register helpers + */ +static u32 filter_rx_s_min_width(struct cx23885_dev *dev, u32 min_width_ns) +{ + u32 count = ns_to_lpf_count(min_width_ns); + cx23888_ir_write4(dev, CX23888_IR_FILTR_REG, count); + return lpf_count_to_ns(count); +} + +/* + * IR IRQ Enable Register helpers + */ +static inline void irqenable_rx(struct cx23885_dev *dev, u32 mask) +{ + mask &= (IRQEN_RTE | IRQEN_ROE | IRQEN_RSE); + cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, + ~(IRQEN_RTE | IRQEN_ROE | IRQEN_RSE), mask); +} + +static inline void irqenable_tx(struct cx23885_dev *dev, u32 mask) +{ + mask &= IRQEN_TSE; + cx23888_ir_and_or4(dev, CX23888_IR_IRQEN_REG, ~IRQEN_TSE, mask); +} + +/* + * V4L2 Subdevice IR Ops + */ +static int cx23888_ir_irq_handler(struct v4l2_subdev *sd, u32 status, + bool *handled) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); + u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); + u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); + + u32 rx_data[FIFO_RX_DEPTH]; + int i, j, k; + u32 events, v; + int tsr, rsr, rto, ror, tse, rse, rte, roe, kror; + + tsr = stats & STATS_TSR; /* Tx FIFO Service Request */ + rsr = stats & STATS_RSR; /* Rx FIFO Service Request */ + rto = stats & STATS_RTO; /* Rx Pulse Width Timer Time Out */ + ror = stats & STATS_ROR; /* Rx FIFO Over Run */ + + tse = irqen & IRQEN_TSE; /* Tx FIFO Service Request IRQ Enable */ + rse = irqen & IRQEN_RSE; /* Rx FIFO Service Reuqest IRQ Enable */ + rte = irqen & IRQEN_RTE; /* Rx Pulse Width Timer Time Out IRQ Enable */ + roe = irqen & IRQEN_ROE; /* Rx FIFO Over Run IRQ Enable */ + + *handled = false; + v4l2_dbg(2, ir_888_debug, sd, "IRQ Status: %s %s %s %s %s %s\n", + tsr ? "tsr" : " ", rsr ? "rsr" : " ", + rto ? "rto" : " ", ror ? "ror" : " ", + stats & STATS_TBY ? "tby" : " ", + stats & STATS_RBY ? "rby" : " "); + + v4l2_dbg(2, ir_888_debug, sd, "IRQ Enables: %s %s %s %s\n", + tse ? "tse" : " ", rse ? "rse" : " ", + rte ? "rte" : " ", roe ? "roe" : " "); + + /* + * Transmitter interrupt service + */ + if (tse && tsr) { + /* + * TODO: + * Check the watermark threshold setting + * Pull FIFO_TX_DEPTH or FIFO_TX_DEPTH/2 entries from tx_kfifo + * Push the data to the hardware FIFO. + * If there was nothing more to send in the tx_kfifo, disable + * the TSR IRQ and notify the v4l2_device. + * If there was something in the tx_kfifo, check the tx_kfifo + * level and notify the v4l2_device, if it is low. + */ + /* For now, inhibit TSR interrupt until Tx is implemented */ + irqenable_tx(dev, 0); + events = V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ; + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_TX_NOTIFY, &events); + *handled = true; + } + + /* + * Receiver interrupt service + */ + kror = 0; + if ((rse && rsr) || (rte && rto)) { + /* + * Receive data on RSR to clear the STATS_RSR. + * Receive data on RTO, since we may not have yet hit the RSR + * watermark when we receive the RTO. + */ + for (i = 0, v = FIFO_RX_NDV; + (v & FIFO_RX_NDV) && !kror; i = 0) { + for (j = 0; + (v & FIFO_RX_NDV) && j < FIFO_RX_DEPTH; j++) { + v = cx23888_ir_read4(dev, CX23888_IR_FIFO_REG); + rx_data[i++] = v & ~FIFO_RX_NDV; + } + if (i == 0) + break; + j = i * sizeof(u32); + k = kfifo_put(state->rx_kfifo, + (unsigned char *) rx_data, j); + if (k != j) + kror++; /* rx_kfifo over run */ + } + *handled = true; + } + + events = 0; + v = 0; + if (kror) { + events |= V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver software FIFO overrun\n"); + } + if (roe && ror) { + /* + * The RX FIFO Enable (CNTRL_RFE) must be toggled to clear + * the Rx FIFO Over Run status (STATS_ROR) + */ + v |= CNTRL_RFE; + events |= V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN; + v4l2_err(sd, "IR receiver hardware FIFO overrun\n"); + } + if (rte && rto) { + /* + * The IR Receiver Enable (CNTRL_RXE) must be toggled to clear + * the Rx Pulse Width Timer Time Out (STATS_RTO) + */ + v |= CNTRL_RXE; + events |= V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED; + } + if (v) { + /* Clear STATS_ROR & STATS_RTO as needed by reseting hardware */ + cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl & ~v); + cx23888_ir_write4(dev, CX23888_IR_CNTRL_REG, cntrl); + *handled = true; + } + if (kfifo_len(state->rx_kfifo) >= CX23888_IR_RX_KFIFO_SIZE / 2) + events |= V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ; + + if (events) + v4l2_subdev_notify(sd, V4L2_SUBDEV_IR_RX_NOTIFY, &events); + return 0; +} + +/* Receiver */ +static int cx23888_ir_rx_read(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx23888_ir_state *state = to_state(sd); + bool invert = (bool) atomic_read(&state->rx_invert); + u16 divider = (u16) atomic_read(&state->rxclk_divider); + + unsigned int i, n; + u32 *p; + u32 u, v; + + n = count / sizeof(u32) * sizeof(u32); + if (n == 0) { + *num = 0; + return 0; + } + + n = kfifo_get(state->rx_kfifo, buf, n); + + n /= sizeof(u32); + *num = n * sizeof(u32); + + for (p = (u32 *) buf, i = 0; i < n; p++, i++) { + if ((*p & FIFO_RXTX_RTO) == FIFO_RXTX_RTO) { + *p = V4L2_SUBDEV_IR_PULSE_RX_SEQ_END; + v4l2_dbg(2, ir_888_debug, sd, "rx read: end of rx\n"); + continue; + } + + u = (*p & FIFO_RXTX_LVL) ? V4L2_SUBDEV_IR_PULSE_LEVEL_MASK : 0; + if (invert) + u = u ? 0 : V4L2_SUBDEV_IR_PULSE_LEVEL_MASK; + + v = (u32) pulse_width_count_to_ns((u16) (*p & FIFO_RXTX), + divider); + if (v >= V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS) + v = V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS - 1; + + *p = u | v; + + v4l2_dbg(2, ir_888_debug, sd, "rx read: %10u ns %s\n", + v, u ? "mark" : "space"); + } + return 0; +} + +static int cx23888_ir_rx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + mutex_lock(&state->rx_params_lock); + memcpy(p, &state->rx_params, sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&state->rx_params_lock); + return 0; +} + +static int cx23888_ir_rx_shutdown(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + mutex_lock(&state->rx_params_lock); + + /* Disable or slow down all IR Rx circuits and counters */ + irqenable_rx(dev, 0); + control_rx_enable(dev, false); + control_rx_demodulation_enable(dev, false); + control_rx_s_edge_detection(dev, CNTRL_EDG_NONE); + filter_rx_s_min_width(dev, 0); + cx23888_ir_write4(dev, CX23888_IR_RXCLK_REG, RXCLK_RCD); + + state->rx_params.shutdown = true; + + mutex_unlock(&state->rx_params_lock); + return 0; +} + +static int cx23888_ir_rx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + struct v4l2_subdev_ir_parameters *o = &state->rx_params; + u16 rxclk_divider; + + if (p->shutdown) + return cx23888_ir_rx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + mutex_lock(&state->rx_params_lock); + + o->shutdown = p->shutdown; + + o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + + o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32); + + /* Before we tweak the hardware, we have to disable the receiver */ + irqenable_rx(dev, 0); + control_rx_enable(dev, false); + + control_rx_demodulation_enable(dev, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = rxclk_rx_s_carrier(dev, p->carrier_freq, + &rxclk_divider); + + o->carrier_freq = p->carrier_freq; + + o->duty_cycle = p->duty_cycle = 50; + + control_rx_s_carrier_window(dev, p->carrier_freq, + &p->carrier_range_lower, + &p->carrier_range_upper); + o->carrier_range_lower = p->carrier_range_lower; + o->carrier_range_upper = p->carrier_range_upper; + } else { + p->max_pulse_width = + rxclk_rx_s_max_pulse_width(dev, p->max_pulse_width, + &rxclk_divider); + o->max_pulse_width = p->max_pulse_width; + } + atomic_set(&state->rxclk_divider, rxclk_divider); + + p->noise_filter_min_width = + filter_rx_s_min_width(dev, p->noise_filter_min_width); + o->noise_filter_min_width = p->noise_filter_min_width; + + p->resolution = clock_divider_to_resolution(rxclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_rx_irq_watermark(dev, RX_FIFO_HALF_FULL); + + control_rx_s_edge_detection(dev, CNTRL_EDG_BOTH); + + o->invert = p->invert; + atomic_set(&state->rx_invert, p->invert); + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + kfifo_reset(state->rx_kfifo); + if (p->interrupt_enable) + irqenable_rx(dev, IRQEN_RSE | IRQEN_RTE | IRQEN_ROE); + control_rx_enable(dev, p->enable); + } + + mutex_unlock(&state->rx_params_lock); + return 0; +} + +/* Transmitter */ +static int cx23888_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + /* For now enable the Tx FIFO Service interrupt & pretend we did work */ + irqenable_tx(dev, IRQEN_TSE); + *num = count; + return 0; +} + +static int cx23888_ir_tx_g_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + mutex_lock(&state->tx_params_lock); + memcpy(p, &state->tx_params, sizeof(struct v4l2_subdev_ir_parameters)); + mutex_unlock(&state->tx_params_lock); + return 0; +} + +static int cx23888_ir_tx_shutdown(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + + mutex_lock(&state->tx_params_lock); + + /* Disable or slow down all IR Tx circuits and counters */ + irqenable_tx(dev, 0); + control_tx_enable(dev, false); + control_tx_modulation_enable(dev, false); + cx23888_ir_write4(dev, CX23888_IR_TXCLK_REG, TXCLK_TCD); + + state->tx_params.shutdown = true; + + mutex_unlock(&state->tx_params_lock); + return 0; +} + +static int cx23888_ir_tx_s_parameters(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *p) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + struct v4l2_subdev_ir_parameters *o = &state->tx_params; + u16 txclk_divider; + + if (p->shutdown) + return cx23888_ir_tx_shutdown(sd); + + if (p->mode != V4L2_SUBDEV_IR_MODE_PULSE_WIDTH) + return -ENOSYS; + + mutex_lock(&state->tx_params_lock); + + o->shutdown = p->shutdown; + + o->mode = p->mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH; + + o->bytes_per_data_element = p->bytes_per_data_element = sizeof(u32); + + /* Before we tweak the hardware, we have to disable the transmitter */ + irqenable_tx(dev, 0); + control_tx_enable(dev, false); + + control_tx_modulation_enable(dev, p->modulation); + o->modulation = p->modulation; + + if (p->modulation) { + p->carrier_freq = txclk_tx_s_carrier(dev, p->carrier_freq, + &txclk_divider); + o->carrier_freq = p->carrier_freq; + + p->duty_cycle = cduty_tx_s_duty_cycle(dev, p->duty_cycle); + o->duty_cycle = p->duty_cycle; + } else { + p->max_pulse_width = + txclk_tx_s_max_pulse_width(dev, p->max_pulse_width, + &txclk_divider); + o->max_pulse_width = p->max_pulse_width; + } + atomic_set(&state->txclk_divider, txclk_divider); + + p->resolution = clock_divider_to_resolution(txclk_divider); + o->resolution = p->resolution; + + /* FIXME - make this dependent on resolution for better performance */ + control_tx_irq_watermark(dev, TX_FIFO_HALF_EMPTY); + + control_tx_polarity_invert(dev, p->invert); + o->invert = p->invert; + + o->interrupt_enable = p->interrupt_enable; + o->enable = p->enable; + if (p->enable) { + kfifo_reset(state->tx_kfifo); + if (p->interrupt_enable) + irqenable_tx(dev, IRQEN_TSE); + control_tx_enable(dev, p->enable); + } + + mutex_unlock(&state->tx_params_lock); + return 0; +} + + +/* + * V4L2 Subdevice Core Ops + */ +static int cx23888_ir_log_status(struct v4l2_subdev *sd) +{ + struct cx23888_ir_state *state = to_state(sd); + struct cx23885_dev *dev = state->dev; + char *s; + int i, j; + + u32 cntrl = cx23888_ir_read4(dev, CX23888_IR_CNTRL_REG); + u32 txclk = cx23888_ir_read4(dev, CX23888_IR_TXCLK_REG) & TXCLK_TCD; + u32 rxclk = cx23888_ir_read4(dev, CX23888_IR_RXCLK_REG) & RXCLK_RCD; + u32 cduty = cx23888_ir_read4(dev, CX23888_IR_CDUTY_REG) & CDUTY_CDC; + u32 stats = cx23888_ir_read4(dev, CX23888_IR_STATS_REG); + u32 irqen = cx23888_ir_read4(dev, CX23888_IR_IRQEN_REG); + u32 filtr = cx23888_ir_read4(dev, CX23888_IR_FILTR_REG) & FILTR_LPF; + + v4l2_info(sd, "IR Receiver:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_RXE ? "yes" : "no"); + v4l2_info(sd, "\tDemodulation from a carrier: %s\n", + cntrl & CNTRL_DMD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_RFE ? "enabled" : "disabled"); + switch (cntrl & CNTRL_EDG) { + case CNTRL_EDG_NONE: + s = "disabled"; + break; + case CNTRL_EDG_FALL: + s = "falling edge"; + break; + case CNTRL_EDG_RISE: + s = "rising edge"; + break; + case CNTRL_EDG_BOTH: + s = "rising & falling edges"; + break; + default: + s = "??? edge"; + break; + } + v4l2_info(sd, "\tPulse timers' start/stop trigger: %s\n", s); + v4l2_info(sd, "\tFIFO data on pulse timer overflow: %s\n", + cntrl & CNTRL_R ? "not loaded" : "overflow marker"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_RIC ? "not empty" : "half full or greater"); + v4l2_info(sd, "\tLoopback mode: %s\n", + cntrl & CNTRL_LBM ? "loopback active" : "normal receive"); + if (cntrl & CNTRL_DMD) { + v4l2_info(sd, "\tExpected carrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(rxclk)); + switch (cntrl & CNTRL_WIN) { + case CNTRL_WIN_3_3: + i = 3; + j = 3; + break; + case CNTRL_WIN_4_3: + i = 4; + j = 3; + break; + case CNTRL_WIN_3_4: + i = 3; + j = 4; + break; + case CNTRL_WIN_4_4: + i = 4; + j = 4; + break; + default: + i = 0; + j = 0; + break; + } + v4l2_info(sd, "\tNext carrier edge window: 16 clocks " + "-%1d/+%1d, %u to %u Hz\n", i, j, + clock_divider_to_freq(rxclk, 16 + j), + clock_divider_to_freq(rxclk, 16 - i)); + } else { + v4l2_info(sd, "\tMax measurable pulse width: %u us, " + "%llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, rxclk), + pulse_width_count_to_ns(FIFO_RXTX, rxclk)); + } + v4l2_info(sd, "\tLow pass filter: %s\n", + filtr ? "enabled" : "disabled"); + if (filtr) + v4l2_info(sd, "\tMin acceptable pulse width (LPF): %u us, " + "%u ns\n", + lpf_count_to_us(filtr), + lpf_count_to_ns(filtr)); + v4l2_info(sd, "\tPulse width timer timed-out: %s\n", + stats & STATS_RTO ? "yes" : "no"); + v4l2_info(sd, "\tPulse width timer time-out intr: %s\n", + irqen & IRQEN_RTE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO overrun: %s\n", + stats & STATS_ROR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO overrun interrupt: %s\n", + irqen & IRQEN_ROE ? "enabled" : "disabled"); + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_RBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_RSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_RSE ? "enabled" : "disabled"); + + v4l2_info(sd, "IR Transmitter:\n"); + v4l2_info(sd, "\tEnabled: %s\n", + cntrl & CNTRL_TXE ? "yes" : "no"); + v4l2_info(sd, "\tModulation onto a carrier: %s\n", + cntrl & CNTRL_MOD ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO: %s\n", + cntrl & CNTRL_TFE ? "enabled" : "disabled"); + v4l2_info(sd, "\tFIFO interrupt watermark: %s\n", + cntrl & CNTRL_TIC ? "not empty" : "half full or less"); + v4l2_info(sd, "\tSignal polarity: %s\n", + cntrl & CNTRL_CPL ? "0:mark 1:space" : "0:space 1:mark"); + if (cntrl & CNTRL_MOD) { + v4l2_info(sd, "\tCarrier (16 clocks): %u Hz\n", + clock_divider_to_carrier_freq(txclk)); + v4l2_info(sd, "\tCarrier duty cycle: %2u/16\n", + cduty + 1); + } else { + v4l2_info(sd, "\tMax pulse width: %u us, " + "%llu ns\n", + pulse_width_count_to_us(FIFO_RXTX, txclk), + pulse_width_count_to_ns(FIFO_RXTX, txclk)); + } + v4l2_info(sd, "\tBusy: %s\n", + stats & STATS_TBY ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service requested: %s\n", + stats & STATS_TSR ? "yes" : "no"); + v4l2_info(sd, "\tFIFO service request interrupt: %s\n", + irqen & IRQEN_TSE ? "enabled" : "disabled"); + + return 0; +} + +static inline int cx23888_ir_dbg_match(const struct v4l2_dbg_match *match) +{ + return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 2; +} + +static int cx23888_ir_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *chip) +{ + struct cx23888_ir_state *state = to_state(sd); + + if (cx23888_ir_dbg_match(&chip->match)) { + chip->ident = state->id; + chip->revision = state->rev; + } + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int cx23888_ir_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct cx23888_ir_state *state = to_state(sd); + u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; + + if (!cx23888_ir_dbg_match(®->match)) + return -EINVAL; + if ((addr & 0x3) != 0) + return -EINVAL; + if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + reg->size = 4; + reg->val = cx23888_ir_read4(state->dev, addr); + return 0; +} + +static int cx23888_ir_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct cx23888_ir_state *state = to_state(sd); + u32 addr = CX23888_IR_REG_BASE + (u32) reg->reg; + + if (!cx23888_ir_dbg_match(®->match)) + return -EINVAL; + if ((addr & 0x3) != 0) + return -EINVAL; + if (addr < CX23888_IR_CNTRL_REG || addr > CX23888_IR_LEARN_REG) + return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + cx23888_ir_write4(state->dev, addr, reg->val); + return 0; +} +#endif + +static const struct v4l2_subdev_core_ops cx23888_ir_core_ops = { + .g_chip_ident = cx23888_ir_g_chip_ident, + .log_status = cx23888_ir_log_status, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = cx23888_ir_g_register, + .s_register = cx23888_ir_s_register, +#endif +}; + +static const struct v4l2_subdev_ir_ops cx23888_ir_ir_ops = { + .interrupt_service_routine = cx23888_ir_irq_handler, + + .rx_read = cx23888_ir_rx_read, + .rx_g_parameters = cx23888_ir_rx_g_parameters, + .rx_s_parameters = cx23888_ir_rx_s_parameters, + + .tx_write = cx23888_ir_tx_write, + .tx_g_parameters = cx23888_ir_tx_g_parameters, + .tx_s_parameters = cx23888_ir_tx_s_parameters, +}; + +static const struct v4l2_subdev_ops cx23888_ir_controller_ops = { + .core = &cx23888_ir_core_ops, + .ir = &cx23888_ir_ir_ops, +}; + +static const struct v4l2_subdev_ir_parameters default_rx_params = { + .bytes_per_data_element = sizeof(u32), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5, RC-6, and RC-6A carrier */ + + /* RC-5: 666,667 ns = 1/36 kHz * 32 cycles * 1 mark * 0.75 */ + /* RC-6A: 333,333 ns = 1/36 kHz * 16 cycles * 1 mark * 0.75 */ + .noise_filter_min_width = 333333, /* ns */ + .carrier_range_lower = 35000, + .carrier_range_upper = 37000, + .invert = false, +}; + +static const struct v4l2_subdev_ir_parameters default_tx_params = { + .bytes_per_data_element = sizeof(u32), + .mode = V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, + + .enable = false, + .interrupt_enable = false, + .shutdown = true, + + .modulation = true, + .carrier_freq = 36000, /* 36 kHz - RC-5 carrier */ + .duty_cycle = 25, /* 25 % - RC-5 carrier */ + .invert = false, +}; + +int cx23888_ir_probe(struct cx23885_dev *dev) +{ + struct cx23888_ir_state *state; + struct v4l2_subdev *sd; + struct v4l2_subdev_ir_parameters default_params; + int ret; + + state = kzalloc(sizeof(struct cx23888_ir_state), GFP_KERNEL); + if (state == NULL) + return -ENOMEM; + + spin_lock_init(&state->rx_kfifo_lock); + state->rx_kfifo = kfifo_alloc(CX23888_IR_RX_KFIFO_SIZE, GFP_KERNEL, + &state->rx_kfifo_lock); + if (state->rx_kfifo == NULL) + return -ENOMEM; + + spin_lock_init(&state->tx_kfifo_lock); + state->tx_kfifo = kfifo_alloc(CX23888_IR_TX_KFIFO_SIZE, GFP_KERNEL, + &state->tx_kfifo_lock); + if (state->tx_kfifo == NULL) { + kfifo_free(state->rx_kfifo); + return -ENOMEM; + } + + state->dev = dev; + state->id = V4L2_IDENT_CX23888_IR; + state->rev = 0; + sd = &state->sd; + + v4l2_subdev_init(sd, &cx23888_ir_controller_ops); + v4l2_set_subdevdata(sd, state); + /* FIXME - fix the formatting of dev->v4l2_dev.name and use it */ + snprintf(sd->name, sizeof(sd->name), "%s/888-ir", dev->name); + sd->grp_id = CX23885_HW_888_IR; + + ret = v4l2_device_register_subdev(&dev->v4l2_dev, sd); + if (ret == 0) { + /* + * Ensure no interrupts arrive from '888 specific conditions, + * since we ignore them in this driver to have commonality with + * similar IR controller cores. + */ + cx23888_ir_write4(dev, CX23888_IR_IRQEN_REG, 0); + + mutex_init(&state->rx_params_lock); + memcpy(&default_params, &default_rx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, rx_s_parameters, &default_params); + + mutex_init(&state->tx_params_lock); + memcpy(&default_params, &default_tx_params, + sizeof(struct v4l2_subdev_ir_parameters)); + v4l2_subdev_call(sd, ir, tx_s_parameters, &default_params); + } else { + kfifo_free(state->rx_kfifo); + kfifo_free(state->tx_kfifo); + } + return ret; +} + +int cx23888_ir_remove(struct cx23885_dev *dev) +{ + struct v4l2_subdev *sd; + struct cx23888_ir_state *state; + + sd = cx23885_find_hw(dev, CX23885_HW_888_IR); + if (sd == NULL) + return -ENODEV; + + cx23888_ir_rx_shutdown(sd); + cx23888_ir_tx_shutdown(sd); + + state = to_state(sd); + v4l2_device_unregister_subdev(sd); + kfifo_free(state->rx_kfifo); + kfifo_free(state->tx_kfifo); + kfree(state); + /* Nothing more to free() as state held the actual v4l2_subdev object */ + return 0; +} diff --git a/drivers/media/video/cx23885/cx23888-ir.h b/drivers/media/video/cx23885/cx23888-ir.h new file mode 100644 index 0000000000000..3d446f9eb94bd --- /dev/null +++ b/drivers/media/video/cx23885/cx23888-ir.h @@ -0,0 +1,28 @@ +/* + * Driver for the Conexant CX23885/7/8 PCIe bridge + * + * CX23888 Integrated Consumer Infrared Controller + * + * Copyright (C) 2009 Andy Walls + * + * 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, or (at your option) any later version. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#ifndef _CX23888_IR_H_ +#define _CX23888_IR_H_ +int cx23888_ir_probe(struct cx23885_dev *dev); +int cx23888_ir_remove(struct cx23885_dev *dev); +#endif diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c index 2f846f5e0f9fe..45608d50529c9 100644 --- a/drivers/media/video/cx25840/cx25840-audio.c +++ b/drivers/media/video/cx25840/cx25840-audio.c @@ -23,87 +23,137 @@ #include "cx25840-core.h" -static int set_audclk_freq(struct i2c_client *client, u32 freq) +/* + * Note: The PLL and SRC parameters are based on a reference frequency that + * would ideally be: + * + * NTSC Color subcarrier freq * 8 = 4.5 MHz/286 * 455/2 * 8 = 28.63636363... MHz + * + * However, it's not the exact reference frequency that matters, only that the + * firmware and modules that comprise the driver for a particular board all + * use the same value (close to the ideal value). + * + * Comments below will note which reference frequency is assumed for various + * parameters. They will usually be one of + * + * ref_freq = 28.636360 MHz + * or + * ref_freq = 28.636363 MHz + */ + +static int cx25840_set_audclk_freq(struct i2c_client *client, u32 freq) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); - if (freq != 32000 && freq != 44100 && freq != 48000) - return -EINVAL; - - /* common for all inputs and rates */ - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */ - if (!state->is_cx23885 && !state->is_cx231xx) - cx25840_write(client, 0x127, 0x50); - if (state->aud_input != CX25840_AUDIO_SERIAL) { switch (freq) { case 32000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1006040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x01bb39ee); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x06, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1006040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x1bb39ee + * 28636363 * 0x6.dd9cf70/0x10 = 32000 * 384 + * 196.6 MHz pre-postdivide + * FIXME < 200 MHz is out of specified valid range + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x01bb39ee); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src3/4/6_ctl = 0x0801f77f */ + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ cx25840_write4(client, 0x900, 0x0801f77f); cx25840_write4(client, 0x904, 0x0801f77f); cx25840_write4(client, 0x90c, 0x0801f77f); break; case 44100: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x1009040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x10 = 44100 * 384 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1009040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - } - if (state->is_cx25836) - break; - - /* src3/4/6_ctl = 0x08016d59 */ + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ cx25840_write4(client, 0x900, 0x08016d59); cx25840_write4(client, 0x904, 0x08016d59); cx25840_write4(client, 0x90c, 0x08016d59); break; case 48000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x100a040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x0098d6e5); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x10 + */ + cx25840_write4(client, 0x108, 0x100a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x10 = 48000 * 384 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 384/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src3/4/6_ctl = 0x08014faa */ + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ cx25840_write4(client, 0x900, 0x08014faa); cx25840_write4(client, 0x904, 0x08014faa); cx25840_write4(client, 0x90c, 0x08014faa); @@ -112,91 +162,249 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) } else { switch (freq) { case 32000: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1e08040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x012a0869); - } + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x08, AUX PLL Post Divider = 0x1e + */ + cx25840_write4(client, 0x108, 0x1e08040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x12a0869 + * 28636363 * 0x8.9504348/0x1e = 32000 * 256 + * 246 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x012a0869); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x14 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x54); - if (state->is_cx25836) + if (is_cx2583x(state)) break; - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ cx25840_write4(client, 0x8f8, 0x08010000); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ cx25840_write4(client, 0x900, 0x08020000); cx25840_write4(client, 0x904, 0x08020000); cx25840_write4(client, 0x90c, 0x08020000); - - /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */ - cx25840_write(client, 0x127, 0x54); break; case 44100: - if (state->is_cx23885) { - /* We don't have register values - * so avoid destroying registers. */ - break; - } - - - if (!state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x1809040f); - - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x00ec6bd6); - } - - if (state->is_cx25836) + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x09, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x1809040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x0ec6bd6 + * 28636363 * 0x9.7635eb0/0x18 = 44100 * 256 + * 271 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x00ec6bd6); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) break; - /* src1_ctl = 0x08010000 */ + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ cx25840_write4(client, 0x8f8, 0x080160cd); - /* src3/4/6_ctl = 0x08020000 */ + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ cx25840_write4(client, 0x900, 0x08017385); cx25840_write4(client, 0x904, 0x08017385); cx25840_write4(client, 0x90c, 0x08017385); break; case 48000: - if (!state->is_cx23885 && !state->is_cx231xx) { - /* VID_PLL and AUX_PLL */ - cx25840_write4(client, 0x108, 0x180a040f); + /* + * VID_PLL Integer = 0x0f, VID_PLL Post Divider = 0x04 + * AUX_PLL Integer = 0x0a, AUX PLL Post Divider = 0x18 + */ + cx25840_write4(client, 0x108, 0x180a040f); + + /* + * VID_PLL Fraction (register 0x10c) = 0x2be2fe + * 28636360 * 0xf.15f17f0/4 = 108 MHz + * 432 MHz pre-postdivide + */ + + /* + * AUX_PLL Fraction = 0x098d6e5 + * 28636363 * 0xa.4c6b728/0x18 = 48000 * 256 + * 295 MHz pre-postdivide + * FIXME 28636363 ref_freq doesn't match VID PLL ref + */ + cx25840_write4(client, 0x110, 0x0098d6e5); + + /* + * SA_MCLK_SEL = 1 + * SA_MCLK_DIV = 0x10 = 256/384 * AUX_PLL post dvivider + */ + cx25840_write(client, 0x127, 0x50); + + if (is_cx2583x(state)) + break; - /* AUX_PLL_FRAC */ - cx25840_write4(client, 0x110, 0x0098d6e5); - } + /* src1_ctl */ + /* 0x1.8000 = 48000/32000 */ + cx25840_write4(client, 0x8f8, 0x08018000); - if (state->is_cx25836) - break; + /* src3/4/6_ctl */ + /* 0x1.5555 = 2 * (32000/48000) */ + cx25840_write4(client, 0x900, 0x08015555); + cx25840_write4(client, 0x904, 0x08015555); + cx25840_write4(client, 0x90c, 0x08015555); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static inline int cx25836_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + return cx25840_set_audclk_freq(client, freq); +} + +static int cx23885_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + case 44100: + case 48000: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + } + } else { + switch (freq) { + case 32000: + case 44100: + /* We don't have register values + * so avoid destroying registers. */ + /* FIXME return -EINVAL; */ + break; + + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } + + state->audclk_freq = freq; + + return 0; +} + +static int cx231xx_set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (state->aud_input != CX25840_AUDIO_SERIAL) { + switch (freq) { + case 32000: + /* src3/4/6_ctl */ + /* 0x1.f77f = (4 * 28636360/8 * 2/455) / 32000 */ + cx25840_write4(client, 0x900, 0x0801f77f); + cx25840_write4(client, 0x904, 0x0801f77f); + cx25840_write4(client, 0x90c, 0x0801f77f); + break; + + case 44100: + /* src3/4/6_ctl */ + /* 0x1.6d59 = (4 * 28636360/8 * 2/455) / 44100 */ + cx25840_write4(client, 0x900, 0x08016d59); + cx25840_write4(client, 0x904, 0x08016d59); + cx25840_write4(client, 0x90c, 0x08016d59); + break; + + case 48000: + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); + break; + } + } else { + switch (freq) { + /* FIXME These cases make different assumptions about audclk */ + case 32000: + /* src1_ctl */ + /* 0x1.0000 = 32000/32000 */ + cx25840_write4(client, 0x8f8, 0x08010000); - if (!state->is_cx23885 && !state->is_cx231xx) { - /* src1_ctl */ - cx25840_write4(client, 0x8f8, 0x08018000); + /* src3/4/6_ctl */ + /* 0x2.0000 = 2 * (32000/32000) */ + cx25840_write4(client, 0x900, 0x08020000); + cx25840_write4(client, 0x904, 0x08020000); + cx25840_write4(client, 0x90c, 0x08020000); + break; - /* src3/4/6_ctl */ - cx25840_write4(client, 0x900, 0x08015555); - cx25840_write4(client, 0x904, 0x08015555); - cx25840_write4(client, 0x90c, 0x08015555); - } else { + case 44100: + /* src1_ctl */ + /* 0x1.60cd = 44100/32000 */ + cx25840_write4(client, 0x8f8, 0x080160cd); - cx25840_write4(client, 0x8f8, 0x0801867c); + /* src3/4/6_ctl */ + /* 0x1.7385 = 2 * (32000/44100) */ + cx25840_write4(client, 0x900, 0x08017385); + cx25840_write4(client, 0x904, 0x08017385); + cx25840_write4(client, 0x90c, 0x08017385); + break; - cx25840_write4(client, 0x900, 0x08014faa); - cx25840_write4(client, 0x904, 0x08014faa); - cx25840_write4(client, 0x90c, 0x08014faa); - } + case 48000: + /* src1_ctl */ + /* 0x1.867c = 48000 / (2 * 28636360/8 * 2/455) */ + cx25840_write4(client, 0x8f8, 0x0801867c); + + /* src3/4/6_ctl */ + /* 0x1.4faa = (4 * 28636360/8 * 2/455) / 48000 */ + cx25840_write4(client, 0x900, 0x08014faa); + cx25840_write4(client, 0x904, 0x08014faa); + cx25840_write4(client, 0x90c, 0x08014faa); break; } } @@ -206,6 +414,25 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq) return 0; } +static int set_audclk_freq(struct i2c_client *client, u32 freq) +{ + struct cx25840_state *state = to_state(i2c_get_clientdata(client)); + + if (freq != 32000 && freq != 44100 && freq != 48000) + return -EINVAL; + + if (is_cx231xx(state)) + return cx231xx_set_audclk_freq(client, freq); + + if (is_cx2388x(state)) + return cx23885_set_audclk_freq(client, freq); + + if (is_cx2583x(state)) + return cx25836_set_audclk_freq(client, freq); + + return cx25840_set_audclk_freq(client, freq); +} + void cx25840_audio_set_path(struct i2c_client *client) { struct cx25840_state *state = to_state(i2c_get_clientdata(client)); @@ -243,7 +470,7 @@ void cx25840_audio_set_path(struct i2c_client *client) cx25840_and_or(client, 0x810, ~0x1, 0x00); /* Ensure the controller is running when we exit */ - if (state->is_cx23885 || state->is_cx231xx) + if (is_cx2388x(state) || is_cx231xx(state)) cx25840_and_or(client, 0x803, ~0x10, 0x10); } @@ -383,7 +610,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) struct cx25840_state *state = to_state(sd); int retval; - if (!state->is_cx25836) + if (!is_cx2583x(state)) cx25840_and_or(client, 0x810, ~0x1, 1); if (state->aud_input != CX25840_AUDIO_SERIAL) { cx25840_and_or(client, 0x803, ~0x10, 0); @@ -392,7 +619,7 @@ int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq) retval = set_audclk_freq(client, freq); if (state->aud_input != CX25840_AUDIO_SERIAL) cx25840_and_or(client, 0x803, ~0x10, 0x10); - if (!state->is_cx25836) + if (!is_cx2583x(state)) cx25840_and_or(client, 0x810, ~0x1, 0); return retval; } diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c index 1aeaf18a9bea3..385ecd58f1c06 100644 --- a/drivers/media/video/cx25840/cx25840-core.c +++ b/drivers/media/video/cx25840/cx25840-core.c @@ -259,6 +259,13 @@ static void cx23885_initialize(struct i2c_client *client) struct cx25840_state *state = to_state(i2c_get_clientdata(client)); struct workqueue_struct *q; + /* + * Come out of digital power down + * The CX23888, at least, needs this, otherwise registers aside from + * 0x0-0x2 can't be read or written. + */ + cx25840_write(client, 0x000, 0); + /* Internal Reset */ cx25840_and_or(client, 0x102, ~0x01, 0x01); cx25840_and_or(client, 0x102, ~0x01, 0x00); @@ -269,18 +276,45 @@ static void cx23885_initialize(struct i2c_client *client) /* DIF in reset? */ cx25840_write(client, 0x398, 0); - /* Trust the default xtal, no division */ - /* This changes for the cx23888 products */ + /* + * Trust the default xtal, no division + * '885: 28.636363... MHz + * '887: 25.000000 MHz + * '888: 50.000000 MHz + */ cx25840_write(client, 0x2, 0x76); - /* Bring down the regulator for AUX clk */ + /* Power up all the PLL's and DLL */ cx25840_write(client, 0x1, 0x40); - /* Sys PLL frac */ - cx25840_write4(client, 0x11c, 0x01d1744c); - - /* Sys PLL int */ - cx25840_write4(client, 0x118, 0x00000416); + /* Sys PLL */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0xb + 0xe8ba26/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x00e8ba26); + cx25840_write4(client, 0x118, 0x0000040b); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0x16 + 0x1d1744c/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x01d1744c); + cx25840_write4(client, 0x118, 0x00000416); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0x14 + 0x0/0x2000000)/4 = 5 * 28.636363 MHz + * 572.73 MHz before post divide + */ + cx25840_write4(client, 0x11c, 0x00000000); + cx25840_write4(client, 0x118, 0x00000414); + break; + } /* Disable DIF bypass */ cx25840_write4(client, 0x33c, 0x00000001); @@ -288,11 +322,15 @@ static void cx23885_initialize(struct i2c_client *client) /* DIF Src phase inc */ cx25840_write4(client, 0x340, 0x0df7df83); - /* Vid PLL frac */ - cx25840_write4(client, 0x10c, 0x01b6db7b); - - /* Vid PLL int */ - cx25840_write4(client, 0x108, 0x00000512); + /* + * Vid PLL + * Setup for a BT.656 pixel clock of 13.5 Mpixels/second + * + * 28.636363 MHz * (0xf + 0x02be2c9/0x2000000)/4 = 8 * 13.5 MHz + * 432.0 MHz before post divide + */ + cx25840_write4(client, 0x10c, 0x002be2c9); + cx25840_write4(client, 0x108, 0x0000040f); /* Luma */ cx25840_write4(client, 0x414, 0x00107d12); @@ -300,11 +338,43 @@ static void cx23885_initialize(struct i2c_client *client) /* Chroma */ cx25840_write4(client, 0x420, 0x3d008282); - /* Aux PLL frac */ - cx25840_write4(client, 0x114, 0x017dbf48); - - /* Aux PLL int */ - cx25840_write4(client, 0x110, 0x000a030e); + /* + * Aux PLL + * Initial setup for audio sample clock: + * 48 ksps, 16 bits/sample, x160 multiplier = 122.88 MHz + * Intial I2S output/master clock(?): + * 48 ksps, 16 bits/sample, x16 multiplier = 12.288 MHz + */ + switch (state->id) { + case V4L2_IDENT_CX23888_AV: + /* + * 50.0 MHz * (0x7 + 0x0bedfa4/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x00bedfa4); + cx25840_write4(client, 0x110, 0x000a0307); + break; + case V4L2_IDENT_CX23887_AV: + /* + * 25.0 MHz * (0xe + 0x17dbf48/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x017dbf48); + cx25840_write4(client, 0x110, 0x000a030e); + break; + case V4L2_IDENT_CX23885_AV: + default: + /* + * 28.636363 MHz * (0xc + 0x1bf0c9e/0x2000000)/3 = 122.88 MHz + * 368.64 MHz before post divide + * 122.88 MHz / 0xa = 12.288 MHz + */ + cx25840_write4(client, 0x114, 0x01bf0c9e); + cx25840_write4(client, 0x110, 0x000a030c); + break; + }; /* ADC2 input select */ cx25840_write(client, 0x102, 0x10); @@ -494,7 +564,7 @@ void cx25840_std_setup(struct i2c_client *client) } /* DEBUG: Displays configured PLL frequency */ - if (!state->is_cx231xx) { + if (!is_cx231xx(state)) { pll_int = cx25840_read(client, 0x108); pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff; pll_post = cx25840_read(client, 0x109); @@ -615,13 +685,30 @@ static void input_change(struct i2c_client *client) } cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_PAL) { - /* Follow tuner change procedure for PAL */ + /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); - cx25840_write(client, 0x80b, 0x10); + /* Since system PAL-L is pretty much non-existant and + not used by any public broadcast network, force + 6.5 MHz carrier to be interpreted as System DK, + this avoids DK audio detection instability */ + cx25840_write(client, 0x80b, 0x00); } else if (std & V4L2_STD_SECAM) { - /* Select autodetect for SECAM */ + /* Autodetect audio standard and audio system */ cx25840_write(client, 0x808, 0xff); - cx25840_write(client, 0x80b, 0x10); + /* If only one of SECAM-DK / SECAM-L is required, then force + 6.5MHz carrier, else autodetect it */ + if ((std & V4L2_STD_SECAM_DK) && + !(std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System DK */ + cx25840_write(client, 0x80b, 0x00); + } else if (!(std & V4L2_STD_SECAM_DK) && + (std & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC))) { + /* 6.5 MHz carrier to be interpreted as System L */ + cx25840_write(client, 0x80b, 0x08); + } else { + /* 6.5 MHz carrier to be autodetected */ + cx25840_write(client, 0x80b, 0x10); + } } cx25840_and_or(client, 0x810, ~0x01, 0); @@ -633,6 +720,10 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp struct cx25840_state *state = to_state(i2c_get_clientdata(client)); u8 is_composite = (vid_input >= CX25840_COMPOSITE1 && vid_input <= CX25840_COMPOSITE8); + u8 is_component = (vid_input & CX25840_COMPONENT_ON) == + CX25840_COMPONENT_ON; + int luma = vid_input & 0xf0; + int chroma = vid_input & 0xf00; u8 reg; v4l_dbg(1, cx25840_debug, client, @@ -645,18 +736,14 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp reg = vid_input & 0xff; if ((vid_input & CX25840_SVIDEO_ON) == CX25840_SVIDEO_ON) is_composite = 0; - else + else if ((vid_input & CX25840_COMPONENT_ON) == 0) is_composite = 1; v4l_dbg(1, cx25840_debug, client, "mux cfg 0x%x comp=%d\n", reg, is_composite); - } else - if (is_composite) { + } else if (is_composite) { reg = 0xf0 + (vid_input - CX25840_COMPOSITE1); } else { - int luma = vid_input & 0xf0; - int chroma = vid_input & 0xf00; - if ((vid_input & ~0xff0) || luma < CX25840_SVIDEO_LUMA1 || luma > CX25840_SVIDEO_LUMA8 || chroma < CX25840_SVIDEO_CHROMA4 || chroma > CX25840_SVIDEO_CHROMA8) { @@ -678,7 +765,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp * configuration in reg (for the cx23885) so we have no * need to attempt to flip bits for earlier av decoders. */ - if (!state->is_cx23885 && !state->is_cx231xx) { + if (!is_cx2388x(state) && !is_cx231xx(state)) { switch (aud_input) { case CX25840_AUDIO_SERIAL: /* do nothing, use serial audio input */ @@ -698,10 +785,13 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp cx25840_write(client, 0x103, reg); - /* Set INPUT_MODE to Composite (0) or S-Video (1) */ - cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); + /* Set INPUT_MODE to Composite, S-Video or Component */ + if (is_component) + cx25840_and_or(client, 0x401, ~0x6, 0x6); + else + cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02); - if (!state->is_cx23885 && !state->is_cx231xx) { + if (!is_cx2388x(state) && !is_cx231xx(state)) { /* Set CH_SEL_ADC2 to 1 if input comes from CH3 */ cx25840_and_or(client, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0); /* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2&CH3 */ @@ -710,22 +800,31 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp else cx25840_and_or(client, 0x102, ~0x4, 0); } else { - if (is_composite) + /* Set DUAL_MODE_ADC2 to 1 if component*/ + cx25840_and_or(client, 0x102, ~0x4, is_component ? 0x4 : 0x0); + if (is_composite) { /* ADC2 input select channel 2 */ cx25840_and_or(client, 0x102, ~0x2, 0); - else - /* ADC2 input select channel 3 */ - cx25840_and_or(client, 0x102, ~0x2, 2); + } else if (!is_component) { + /* S-Video */ + if (chroma >= CX25840_SVIDEO_CHROMA7) { + /* ADC2 input select channel 3 */ + cx25840_and_or(client, 0x102, ~0x2, 2); + } else { + /* ADC2 input select channel 2 */ + cx25840_and_or(client, 0x102, ~0x2, 0); + } + } } state->vid_input = vid_input; state->aud_input = aud_input; - if (!state->is_cx25836) { + if (!is_cx2583x(state)) { cx25840_audio_set_path(client); input_change(client); } - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Audio channel 1 src : Parallel 1 */ cx25840_write(client, 0x124, 0x03); @@ -741,7 +840,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp */ cx25840_write(client, 0x918, 0xa0); cx25840_write(client, 0x919, 0x01); - } else if (state->is_cx231xx) { + } else if (is_cx231xx(state)) { /* Audio channel 1 src : Parallel 1 */ cx25840_write(client, 0x124, 0x03); @@ -805,7 +904,7 @@ static int set_v4lstd(struct i2c_client *client) cx25840_and_or(client, 0x400, ~0xf, fmt); cx25840_and_or(client, 0x403, ~0x3, pal_m); cx25840_std_setup(client); - if (!state->is_cx25836) + if (!is_cx2583x(state)) input_change(client); return 0; } @@ -868,7 +967,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return cx25840_audio_s_ctrl(sd, ctrl); @@ -905,7 +1004,7 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) case V4L2_CID_AUDIO_TREBLE: case V4L2_CID_AUDIO_BALANCE: case V4L2_CID_AUDIO_MUTE: - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return cx25840_audio_g_ctrl(sd, ctrl); default: @@ -1209,11 +1308,11 @@ static int cx25840_load_fw(struct v4l2_subdev *sd) if (!state->is_initialized) { /* initialize and load firmware */ state->is_initialized = 1; - if (state->is_cx25836) + if (is_cx2583x(state)) cx25836_initialize(client); - else if (state->is_cx23885) + else if (is_cx2388x(state)) cx23885_initialize(client); - else if (state->is_cx231xx) + else if (is_cx231xx(state)) cx231xx_initialize(client); else cx25840_initialize(client); @@ -1256,17 +1355,17 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable) v4l_dbg(1, cx25840_debug, client, "%s output\n", enable ? "enable" : "disable"); if (enable) { - if (state->is_cx23885 || state->is_cx231xx) { + if (is_cx2388x(state) || is_cx231xx(state)) { u8 v = (cx25840_read(client, 0x421) | 0x0b); cx25840_write(client, 0x421, v); } else { cx25840_write(client, 0x115, - state->is_cx25836 ? 0x0c : 0x8c); + is_cx2583x(state) ? 0x0c : 0x8c); cx25840_write(client, 0x116, - state->is_cx25836 ? 0x04 : 0x07); + is_cx2583x(state) ? 0x04 : 0x07); } } else { - if (state->is_cx23885 || state->is_cx231xx) { + if (is_cx2388x(state) || is_cx231xx(state)) { u8 v = cx25840_read(client, 0x421) & ~(0x0b); cx25840_write(client, 0x421, v); } else { @@ -1292,7 +1391,7 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) default: break; } - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; switch (qc->id) { @@ -1346,7 +1445,7 @@ static int cx25840_s_audio_routing(struct v4l2_subdev *sd, struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->is_cx25836) + if (is_cx2583x(state)) return -EINVAL; return set_input(client, state->vid_input, input); } @@ -1356,7 +1455,7 @@ static int cx25840_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fr struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (!state->is_cx25836) + if (!is_cx2583x(state)) input_change(client); return 0; } @@ -1373,7 +1472,7 @@ static int cx25840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) return 0; vt->signal = vpres ? 0xffff : 0x0; - if (state->is_cx25836) + if (is_cx2583x(state)) return 0; vt->capability |= @@ -1404,7 +1503,7 @@ static int cx25840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->radio || state->is_cx25836) + if (state->radio || is_cx2583x(state)) return 0; switch (vt->audmode) { @@ -1445,11 +1544,11 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val) struct cx25840_state *state = to_state(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); - if (state->is_cx25836) + if (is_cx2583x(state)) cx25836_initialize(client); - else if (state->is_cx23885) + else if (is_cx2388x(state)) cx23885_initialize(client); - else if (state->is_cx231xx) + else if (is_cx231xx(state)) cx231xx_initialize(client); else cx25840_initialize(client); @@ -1470,7 +1569,7 @@ static int cx25840_log_status(struct v4l2_subdev *sd) struct i2c_client *client = v4l2_get_subdevdata(sd); log_video_status(client); - if (!state->is_cx25836) + if (!is_cx2583x(state)) log_audio_status(client); return 0; } @@ -1521,12 +1620,50 @@ static const struct v4l2_subdev_ops cx25840_ops = { /* ----------------------------------------------------------------------- */ +static u32 get_cx2388x_ident(struct i2c_client *client) +{ + u32 ret; + + /* Come out of digital power down */ + cx25840_write(client, 0x000, 0); + + /* Detecting whether the part is cx23885/7/8 is more + * difficult than it needs to be. No ID register. Instead we + * probe certain registers indicated in the datasheets to look + * for specific defaults that differ between the silicon designs. */ + + /* It's either 885/7 if the IR Tx Clk Divider register exists */ + if (cx25840_read4(client, 0x204) & 0xffff) { + /* CX23885 returns bogus repetitive byte values for the DIF, + * which doesn't exist for it. (Ex. 8a8a8a8a or 31313131) */ + ret = cx25840_read4(client, 0x300); + if (((ret & 0xffff0000) >> 16) == (ret & 0xffff)) { + /* No DIF */ + ret = V4L2_IDENT_CX23885_AV; + } else { + /* CX23887 has a broken DIF, but the registers + * appear valid (but unsed), good enough to detect. */ + ret = V4L2_IDENT_CX23887_AV; + } + } else if (cx25840_read4(client, 0x300) & 0x0fffffff) { + /* DIF PLL Freq Word reg exists; chip must be a CX23888 */ + ret = V4L2_IDENT_CX23888_AV; + } else { + v4l_err(client, "Unable to detect h/w, assuming cx23887\n"); + ret = V4L2_IDENT_CX23887_AV; + } + + /* Back into digital power down */ + cx25840_write(client, 0x000, 2); + return ret; +} + static int cx25840_probe(struct i2c_client *client, const struct i2c_device_id *did) { struct cx25840_state *state; struct v4l2_subdev *sd; - u32 id; + u32 id = V4L2_IDENT_NONE; u16 device_id; /* Check if the adapter supports the needed features */ @@ -1543,17 +1680,22 @@ static int cx25840_probe(struct i2c_client *client, * 0x83 for the cx2583x and 0x84 for the cx2584x */ if ((device_id & 0xff00) == 0x8300) { id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; - } - else if ((device_id & 0xff00) == 0x8400) { + } else if ((device_id & 0xff00) == 0x8400) { id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); } else if (device_id == 0x0000) { - id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; - } else if (device_id == 0x1313) { - id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6; + id = get_cx2388x_ident(client); } else if ((device_id & 0xfff0) == 0x5A30) { - id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf); - } - else { + /* The CX23100 (0x5A3C = 23100) doesn't have an A/V decoder */ + id = V4L2_IDENT_CX2310X_AV; + } else if ((device_id & 0xff) == (device_id >> 8)) { + v4l_err(client, + "likely a confused/unresponsive cx2388[578] A/V decoder" + " found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + v4l_err(client, "A method to reset it from the cx25840 driver" + " software is not known at this time\n"); + return -ENODEV; + } else { v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n"); return -ENODEV; } @@ -1564,17 +1706,45 @@ static int cx25840_probe(struct i2c_client *client, sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &cx25840_ops); - /* Note: revision '(device_id & 0x0f) == 2' was never built. The - marking skips from 0x1 == 22 to 0x3 == 23. */ - v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", - (device_id & 0xfff0) >> 4, - (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 : (device_id & 0x0f), - client->addr << 1, client->adapter->name); + switch (id) { + case V4L2_IDENT_CX23885_AV: + v4l_info(client, "cx23885 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23887_AV: + v4l_info(client, "cx23887 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX23888_AV: + v4l_info(client, "cx23888 A/V decoder found @ 0x%x (%s)\n", + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX2310X_AV: + v4l_info(client, "cx%d A/V decoder found @ 0x%x (%s)\n", + device_id, client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25840: + case V4L2_IDENT_CX25841: + case V4L2_IDENT_CX25842: + case V4L2_IDENT_CX25843: + /* Note: revision '(device_id & 0x0f) == 2' was never built. The + marking skips from 0x1 == 22 to 0x3 == 23. */ + v4l_info(client, "cx25%3x-2%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, + (device_id & 0x0f) < 3 ? (device_id & 0x0f) + 1 + : (device_id & 0x0f), + client->addr << 1, client->adapter->name); + break; + case V4L2_IDENT_CX25836: + case V4L2_IDENT_CX25837: + default: + v4l_info(client, "cx25%3x-%x found @ 0x%x (%s)\n", + (device_id & 0xfff0) >> 4, device_id & 0x0f, + client->addr << 1, client->adapter->name); + break; + } state->c = client; - state->is_cx25836 = ((device_id & 0xff00) == 0x8300); - state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313); - state->is_cx231xx = (device_id == 0x5a3e); state->vid_input = CX25840_COMPOSITE7; state->aud_input = CX25840_AUDIO8; state->audclk_freq = 48000; diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h index 814b565369949..55345444417f1 100644 --- a/drivers/media/video/cx25840/cx25840-core.h +++ b/drivers/media/video/cx25840/cx25840-core.h @@ -23,6 +23,7 @@ #include #include +#include #include /* ENABLE_PVR150_WORKAROUND activates a workaround for a hardware bug that is @@ -48,9 +49,6 @@ struct cx25840_state { int vbi_line_offset; u32 id; u32 rev; - int is_cx25836; - int is_cx23885; - int is_cx231xx; int is_initialized; wait_queue_head_t fw_wait; /* wake up when the fw load is finished */ struct work_struct fw_work; /* work entry for fw load */ @@ -61,6 +59,24 @@ static inline struct cx25840_state *to_state(struct v4l2_subdev *sd) return container_of(sd, struct cx25840_state, sd); } +static inline bool is_cx2583x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX25836 || + state->id == V4L2_IDENT_CX25837; +} + +static inline bool is_cx231xx(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX2310X_AV; +} + +static inline bool is_cx2388x(struct cx25840_state *state) +{ + return state->id == V4L2_IDENT_CX23885_AV || + state->id == V4L2_IDENT_CX23887_AV || + state->id == V4L2_IDENT_CX23888_AV; +} + /* ----------------------------------------------------------------------- */ /* cx25850-core.c */ int cx25840_write(struct i2c_client *client, u16 addr, u8 value); diff --git a/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c index 1f483c1d0dbe8..8150200511dae 100644 --- a/drivers/media/video/cx25840/cx25840-firmware.c +++ b/drivers/media/video/cx25840/cx25840-firmware.c @@ -67,9 +67,9 @@ static const char *get_fw_name(struct i2c_client *client) if (firmware[0]) return firmware; - if (state->is_cx23885) + if (is_cx2388x(state)) return "v4l-cx23885-avcore-01.fw"; - if (state->is_cx231xx) + if (is_cx231xx(state)) return "v4l-cx231xx-avcore-01.fw"; return "v4l-cx25840.fw"; } @@ -112,13 +112,13 @@ int cx25840_loadfw(struct i2c_client *client) int MAX_BUF_SIZE = FWSEND; u32 gpio_oe = 0, gpio_da = 0; - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Preserve the GPIO OE and output bits */ gpio_oe = cx25840_read(client, 0x160); gpio_da = cx25840_read(client, 0x164); } - if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) { + if (is_cx231xx(state) && MAX_BUF_SIZE > 16) { v4l_err(client, " Firmware download size changed to 16 bytes max length\n"); MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */ } @@ -156,7 +156,7 @@ int cx25840_loadfw(struct i2c_client *client) size = fw->size; release_firmware(fw); - if (state->is_cx23885) { + if (is_cx2388x(state)) { /* Restore GPIO configuration after f/w load */ cx25840_write(client, 0x160, gpio_oe); cx25840_write(client, 0x164, gpio_da); diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index 49952980dab3b..c7e5851d3486c 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -61,6 +61,8 @@ config VIDEO_CX88_DVB select DVB_STV0299 if !DVB_FE_CUSTOMISE select DVB_STV0288 if !DVB_FE_CUSTOMISE select DVB_STB6000 if !DVB_FE_CUSTOMISE + select DVB_STV0900 if !DVB_FE_CUSTOMISE + select DVB_STB6100 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE ---help--- This adds support for DVB/ATSC cards based on the diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c index 33be6369871a6..d844f2aaa01d1 100644 --- a/drivers/media/video/cx88/cx88-cards.c +++ b/drivers/media/video/cx88/cx88-cards.c @@ -2075,6 +2075,18 @@ static const struct cx88_board cx88_boards[] = { }, .mpeg = CX88_MPEG_DVB, }, + [CX88_BOARD_PROF_7301] = { + .name = "Prof 7301 DVB-S/S2", + .tuner_type = UNSET, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .input = { { + .type = CX88_VMUX_DVB, + .vmux = 0, + } }, + .mpeg = CX88_MPEG_DVB, + }, }; /* ------------------------------------------------------------------ */ @@ -2535,6 +2547,10 @@ static const struct cx88_subid cx88_subids[] = { .subvendor = 0x107d, .subdevice = 0x6618, .card = CX88_BOARD_WINFAST_TV2000_XP_GLOBAL, + }, { + .subvendor = 0xb034, + .subdevice = 0x3034, + .card = CX88_BOARD_PROF_7301, }, }; @@ -3211,6 +3227,7 @@ static void cx88_card_setup(struct cx88_core *core) case CX88_BOARD_TBS_8920: case CX88_BOARD_PROF_6200: case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: case CX88_BOARD_SATTRADE_ST4200: cx_write(MO_GP0_IO, 0x8000); msleep(100); @@ -3267,7 +3284,7 @@ static void cx88_card_setup(struct cx88_core *core) ctl.fname); call_all(core, tuner, s_config, &xc2028_cfg); } - call_all(core, tuner, s_standby); + call_all(core, core, s_power, 0); } /* ------------------------------------------------------------------ */ diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c index 518bcfe18bcbf..b142969232508 100644 --- a/drivers/media/video/cx88/cx88-dvb.c +++ b/drivers/media/video/cx88/cx88-dvb.c @@ -53,6 +53,9 @@ #include "stv0288.h" #include "stb6000.h" #include "cx24116.h" +#include "stv0900.h" +#include "stb6100.h" +#include "stb6100_proc.h" MODULE_DESCRIPTION("driver for cx2388x based DVB cards"); MODULE_AUTHOR("Chris Pascoe "); @@ -573,6 +576,15 @@ static int cx24116_set_ts_param(struct dvb_frontend *fe, return 0; } +static int stv0900_set_ts_param(struct dvb_frontend *fe, + int is_punctured) +{ + struct cx8802_dev *dev = fe->dvb->priv; + dev->ts_gen_cntrl = 0; + + return 0; +} + static int cx24116_reset_device(struct dvb_frontend *fe) { struct cx8802_dev *dev = fe->dvb->priv; @@ -601,6 +613,23 @@ static struct cx24116_config tevii_s460_config = { .reset_device = cx24116_reset_device, }; +static struct stv0900_config prof_7301_stv0900_config = { + .demod_address = 0x6a, +/* demod_mode = 0,*/ + .xtal = 27000000, + .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */ + .diseqc_mode = 2,/* 2/3 PWM */ + .tun1_maddress = 0,/* 0x60 */ + .tun1_adc = 0,/* 2 Vpp */ + .path1_mode = 3, + .set_ts_params = stv0900_set_ts_param, +}; + +static struct stb6100_config prof_7301_stb6100_config = { + .tuner_address = 0x60, + .refclock = 27000000, +}; + static struct stv0299_config tevii_tuner_sharp_config = { .demod_address = 0x68, .inittab = sharp_z0194a_inittab, @@ -1149,6 +1178,31 @@ static int dvb_register(struct cx8802_dev *dev) goto frontend_detach; } break; + case CX88_BOARD_PROF_7301:{ + struct dvb_tuner_ops *tuner_ops = NULL; + + fe0->dvb.frontend = dvb_attach(stv0900_attach, + &prof_7301_stv0900_config, + &core->i2c_adap, 0); + if (fe0->dvb.frontend != NULL) { + if (!dvb_attach(stb6100_attach, fe0->dvb.frontend, + &prof_7301_stb6100_config, + &core->i2c_adap)) + goto frontend_detach; + + tuner_ops = &fe0->dvb.frontend->ops.tuner_ops; + tuner_ops->set_frequency = stb6100_set_freq; + tuner_ops->get_frequency = stb6100_get_freq; + tuner_ops->set_bandwidth = stb6100_set_bandw; + tuner_ops->get_bandwidth = stb6100_get_bandw; + + core->prev_set_voltage = + fe0->dvb.frontend->ops.set_voltage; + fe0->dvb.frontend->ops.set_voltage = + tevii_dvbs_set_voltage; + } + break; + } default: printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n", core->name); @@ -1170,11 +1224,11 @@ static int dvb_register(struct cx8802_dev *dev) fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl; /* Put the analog decoder in standby to keep it quiet */ - call_all(core, tuner, s_standby); + call_all(core, core, s_power, 0); /* register everything */ return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, - &dev->pci->dev, adapter_nr, mfe_shared); + &dev->pci->dev, adapter_nr, mfe_shared, NULL); frontend_detach: core->gate_ctrl = NULL; diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 78b3635178afe..92b8cdf9fb815 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -118,13 +118,13 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) data = (data << 4) | ((gpio_key & 0xf0) >> 4); - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); ir_input_nokey(ir->input, &ir->ir); } else if (ir->mask_keydown) { /* bit set on keydown */ if (gpio & ir->mask_keydown) { - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); } else { ir_input_nokey(ir->input, &ir->ir); } @@ -132,14 +132,14 @@ static void cx88_ir_handle_key(struct cx88_IR *ir) } else if (ir->mask_keyup) { /* bit cleared on keydown */ if (0 == (gpio & ir->mask_keyup)) { - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); } else { ir_input_nokey(ir->input, &ir->ir); } } else { /* can't distinguish keydown/up :-/ */ - ir_input_keydown(ir->input, &ir->ir, data, data); + ir_input_keydown(ir->input, &ir->ir, data); ir_input_nokey(ir->input, &ir->ir); } } @@ -303,6 +303,23 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) ir->mask_keydown = 0x02; ir->polling = 50; /* ms */ break; + case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_SATTRADE_ST4200: + case CX88_BOARD_TBS_8920: + case CX88_BOARD_TBS_8910: + case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: + case CX88_BOARD_PROF_6200: + ir_codes = &ir_codes_tbs_nec_table; + ir_type = IR_TYPE_PD; + ir->sampling = 0xff00; /* address */ + break; + case CX88_BOARD_TEVII_S460: + case CX88_BOARD_TEVII_S420: + ir_codes = &ir_codes_tevii_nec_table; + ir_type = IR_TYPE_PD; + ir->sampling = 0xff00; /* address */ + break; case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: ir_codes = &ir_codes_dntv_live_dvbt_pro_table; ir_type = IR_TYPE_PD; @@ -343,7 +360,10 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name); snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -373,6 +393,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci) cx88_ir_stop(core, ir); core->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -387,6 +408,7 @@ int cx88_ir_fini(struct cx88_core *core) return 0; cx88_ir_stop(core, ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); @@ -432,8 +454,17 @@ void cx88_ir_irq(struct cx88_core *core) /* decode it */ switch (core->boardnr) { + case CX88_BOARD_TEVII_S460: + case CX88_BOARD_TEVII_S420: case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1: case CX88_BOARD_DNTV_LIVE_DVB_T_PRO: + case CX88_BOARD_OMICOM_SS4_PCI: + case CX88_BOARD_SATTRADE_ST4200: + case CX88_BOARD_TBS_8920: + case CX88_BOARD_TBS_8910: + case CX88_BOARD_PROF_7300: + case CX88_BOARD_PROF_7301: + case CX88_BOARD_PROF_6200: ircode = ir_decode_pulsedistance(ir->samples, ir->scount, 1, 4); if (ircode == 0xffffffff) { /* decoding error */ @@ -461,7 +492,7 @@ void cx88_ir_irq(struct cx88_core *core) ir_dprintk("Key Code: %x\n", (ircode >> 16) & 0x7f); - ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f, (ircode >> 16) & 0xff); + ir_input_keydown(ir->input, &ir->ir, (ircode >> 16) & 0x7f); ir->release = jiffies + msecs_to_jiffies(120); break; case CX88_BOARD_HAUPPAUGE: @@ -498,7 +529,7 @@ void cx88_ir_irq(struct cx88_core *core) if ( dev != 0x1e && dev != 0x1f ) /* not a hauppauge remote */ break; - ir_input_keydown(ir->input, &ir->ir, code, ircode); + ir_input_keydown(ir->input, &ir->ir, code); ir->release = jiffies + msecs_to_jiffies(120); break; case CX88_BOARD_PINNACLE_PCTV_HD_800i: @@ -506,7 +537,7 @@ void cx88_ir_irq(struct cx88_core *core) ir_dprintk("biphase decoded: %x\n", ircode); if ((ircode & 0xfffff000) != 0x3000) break; - ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f, ircode); + ir_input_keydown(ir->input, &ir->ir, ircode & 0x3f); ir->release = jiffies + msecs_to_jiffies(120); break; } diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c index 57e6b1241090d..d7e8fcee559c0 100644 --- a/drivers/media/video/cx88/cx88-video.c +++ b/drivers/media/video/cx88/cx88-video.c @@ -935,7 +935,7 @@ static int video_release(struct file *file) mutex_lock(&dev->core->lock); if(atomic_dec_and_test(&dev->core->users)) - call_all(dev->core, tuner, s_standby); + call_all(dev->core, core, s_power, 0); mutex_unlock(&dev->core->lock); return 0; diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h index d5cea41f42078..e1c5217101031 100644 --- a/drivers/media/video/cx88/cx88.h +++ b/drivers/media/video/cx88/cx88.h @@ -238,6 +238,7 @@ extern struct sram_channel cx88_sram_channels[]; #define CX88_BOARD_HAUPPAUGE_IRONLY 80 #define CX88_BOARD_WINFAST_DTV1800H 81 #define CX88_BOARD_WINFAST_DTV2000H_J 82 +#define CX88_BOARD_PROF_7301 83 enum cx88_itype { CX88_VMUX_COMPOSITE1 = 1, diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 402ce43ef38ee..12a1b3d7132de 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -660,7 +660,7 @@ static void vpfe_detach_irq(struct vpfe_device *vpfe_dev) frame_format = ccdc_dev->hw_ops.get_frame_format(); if (frame_format == CCDC_FRMFMT_PROGRESSIVE) - free_irq(IRQ_VDINT1, vpfe_dev); + free_irq(vpfe_dev->ccdc_irq1, vpfe_dev); } static int vpfe_attach_irq(struct vpfe_device *vpfe_dev) @@ -1338,7 +1338,7 @@ static int vpfe_reqbufs(struct file *file, void *priv, vpfe_dev->memory = req_buf->memory; videobuf_queue_dma_contig_init(&vpfe_dev->buffer_queue, &vpfe_videobuf_qops, - NULL, + vpfe_dev->pdev, &vpfe_dev->irqlock, req_buf->type, vpfe_dev->fmt.fmt.pix.field, @@ -1413,6 +1413,41 @@ static int vpfe_dqbuf(struct file *file, void *priv, buf, file->f_flags & O_NONBLOCK); } +static int vpfe_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, queryctrl, qctrl); + +} + +static int vpfe_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, g_ctrl, ctrl); +} + +static int vpfe_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) +{ + struct vpfe_device *vpfe_dev = video_drvdata(file); + struct vpfe_subdev_info *sdinfo; + + sdinfo = vpfe_dev->current_subdev; + + return v4l2_device_call_until_err(&vpfe_dev->v4l2_dev, sdinfo->grp_id, + core, s_ctrl, ctrl); +} + /* * vpfe_calculate_offsets : This function calculates buffers offset * for top and bottom field @@ -1577,7 +1612,7 @@ static int vpfe_cropcap(struct file *file, void *priv, v4l2_dbg(1, debug, &vpfe_dev->v4l2_dev, "vpfe_cropcap\n"); - if (vpfe_dev->std_index > ARRAY_SIZE(vpfe_standards)) + if (vpfe_dev->std_index >= ARRAY_SIZE(vpfe_standards)) return -EINVAL; memset(crop, 0, sizeof(struct v4l2_cropcap)); @@ -1710,6 +1745,9 @@ static const struct v4l2_ioctl_ops vpfe_ioctl_ops = { .vidioc_querystd = vpfe_querystd, .vidioc_s_std = vpfe_s_std, .vidioc_g_std = vpfe_g_std, + .vidioc_queryctrl = vpfe_queryctrl, + .vidioc_g_ctrl = vpfe_g_ctrl, + .vidioc_s_ctrl = vpfe_s_ctrl, .vidioc_reqbufs = vpfe_reqbufs, .vidioc_querybuf = vpfe_querybuf, .vidioc_qbuf = vpfe_qbuf, @@ -1978,8 +2016,7 @@ static __init int vpfe_probe(struct platform_device *pdev) platform_set_drvdata(pdev, vpfe_dev); /* set driver private data */ video_set_drvdata(vpfe_dev->video_dev, vpfe_dev); - i2c_adap = i2c_get_adapter(1); - vpfe_cfg = pdev->dev.platform_data; + i2c_adap = i2c_get_adapter(vpfe_cfg->i2c_adapter_id); num_subdevs = vpfe_cfg->num_subdevs; vpfe_dev->sd = kmalloc(sizeof(struct v4l2_subdev *) * num_subdevs, GFP_KERNEL); diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index ac947aecb9c37..bd783387b37d0 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -293,7 +293,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) dprintk("opening device and trying to acquire exclusive lock\n"); if (!dev) { - printk(KERN_ERR "BUG: em28xx can't find device struct." + em28xx_err("BUG: em28xx can't find device struct." " Can't proceed with open\n"); return -ENODEV; } @@ -325,7 +325,7 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream) return 0; err: - printk(KERN_ERR "Error while configuring em28xx mixer\n"); + em28xx_err("Error while configuring em28xx mixer\n"); return ret; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index c0fd5c6feeac9..82da205047bef 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -225,6 +225,14 @@ static struct em28xx_reg_seq silvercrest_reg_seq[] = { { -1, -1, -1, -1}, }; +static struct em28xx_reg_seq vc211a_enable[] = { + {EM28XX_R08_GPIO, 0xff, 0x07, 10}, + {EM28XX_R08_GPIO, 0xff, 0x0f, 10}, + {EM28XX_R08_GPIO, 0xff, 0x0b, 10}, + { -1, -1, -1, -1}, +}; + + /* * Board definitions */ @@ -829,7 +837,7 @@ struct em28xx_board em28xx_boards[] = { .mts_firmware = 1, .has_dvb = 1, .dvb_gpio = hauppauge_wintv_hvr_900_digital, - .ir_codes = &ir_codes_hauppauge_new_table, + .ir_codes = &ir_codes_rc5_hauppauge_new_table, .decoder = EM28XX_TVP5150, .input = { { .type = EM28XX_VMUX_TELEVISION, @@ -1009,6 +1017,23 @@ struct em28xx_board em28xx_boards[] = { .amux = EM28XX_AMUX_LINE_IN, } }, }, + [EM2800_BOARD_VC211A] = { + .name = "Actionmaster/LinXcel/Digitus VC211A", + .is_em2800 = 1, + .tuner_type = TUNER_ABSENT, /* Capture-only board */ + .decoder = EM28XX_SAA711X, + .input = { { + .type = EM28XX_VMUX_COMPOSITE1, + .vmux = SAA7115_COMPOSITE0, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = vc211a_enable, + }, { + .type = EM28XX_VMUX_SVIDEO, + .vmux = SAA7115_SVIDEO3, + .amux = EM28XX_AMUX_LINE_IN, + .gpio = vc211a_enable, + } }, + }, [EM2800_BOARD_LEADTEK_WINFAST_USBII] = { .name = "Leadtek Winfast USB II", .is_em2800 = 1, @@ -1381,10 +1406,14 @@ struct em28xx_board em28xx_boards[] = { }, [EM2882_BOARD_TERRATEC_HYBRID_XS] = { .name = "Terratec Hybrid XS (em2882)", - .valid = EM28XX_BOARD_NOT_VALIDATED, .tuner_type = TUNER_XC2028, .tuner_gpio = default_tuner_gpio, + .mts_firmware = 1, .decoder = EM28XX_TVP5150, + .has_dvb = 1, + .dvb_gpio = hauppauge_wintv_hvr_900_digital, + .ir_codes = &ir_codes_terratec_cinergy_xs_table, + .xclk = EM28XX_XCLK_FREQUENCY_12MHZ, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1608,6 +1637,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2861), .driver_info = EM2820_BOARD_UNKNOWN }, + { USB_DEVICE(0xeb1a, 0x2862), + .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2870), .driver_info = EM2820_BOARD_UNKNOWN }, { USB_DEVICE(0xeb1a, 0x2881), @@ -2050,6 +2081,7 @@ static void em28xx_setup_xc3028(struct em28xx *dev, struct xc2028_ctrl *ctl) switch (dev->model) { case EM2880_BOARD_EMPIRE_DUAL_TV: case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: ctl->demod = XC3028_FE_ZARLINK456; break; case EM2880_BOARD_TERRATEC_HYBRID_XS: @@ -2227,6 +2259,7 @@ static int em28xx_hint_board(struct em28xx *dev) /* ----------------------------------------------------------------------- */ void em28xx_register_i2c_ir(struct em28xx *dev) { + struct i2c_board_info info; const unsigned short addr_list[] = { 0x30, 0x47, I2C_CLIENT_END }; @@ -2234,9 +2267,9 @@ void em28xx_register_i2c_ir(struct em28xx *dev) if (disable_ir) return; - memset(&dev->info, 0, sizeof(&dev->info)); + memset(&info, 0, sizeof(struct i2c_board_info)); memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); /* detect & configure */ switch (dev->model) { @@ -2259,8 +2292,8 @@ void em28xx_register_i2c_ir(struct em28xx *dev) } if (dev->init_data.name) - dev->info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); + info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list); } void em28xx_card_setup(struct em28xx *dev) @@ -2524,6 +2557,9 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, dev->chip_id = retval; switch (dev->chip_id) { + case CHIP_ID_EM2800: + em28xx_info("chip ID is em2800\n"); + break; case CHIP_ID_EM2710: em28xx_info("chip ID is em2710\n"); break; @@ -2650,7 +2686,7 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, em28xx_init_extension(dev); /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); return 0; diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index a88257a7d94ff..3f86d36dff2b0 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -50,7 +50,7 @@ MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]"); printk(KERN_INFO "%s %s :"fmt, \ dev->name, __func__ , ##arg); } while (0) -static int alt = EM28XX_PINOUT; +static int alt; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); @@ -533,8 +533,15 @@ int em28xx_audio_setup(struct em28xx *dev) vid1 = em28xx_read_ac97(dev, AC97_VENDOR_ID1); if (vid1 < 0) { - /* Device likely doesn't support AC97 */ + /* + * Device likely doesn't support AC97 + * Note: (some) em2800 devices without eeprom reports 0x91 on + * CHIPCFG register, even not having an AC97 chip + */ em28xx_warn("AC97 chip type couldn't be determined\n"); + dev->audio_mode.ac97 = EM28XX_NO_AC97; + dev->has_alsa_audio = 0; + dev->audio_mode.has_audio = 0; goto init_audio; } @@ -778,6 +785,16 @@ int em28xx_set_alternate(struct em28xx *dev) int i; unsigned int min_pkt_size = dev->width * 2 + 4; + /* + * alt = 0 is used only for control messages, so, only values + * greater than 0 can be used for streaming. + */ + if (alt && alt < dev->num_alt) { + em28xx_coredbg("alternate forced to %d\n", dev->alt); + dev->alt = alt; + goto set_alt; + } + /* When image size is bigger than a certain value, the frame size should be increased, otherwise, only green screen will be received. @@ -798,6 +815,7 @@ int em28xx_set_alternate(struct em28xx *dev) dev->alt = i; } +set_alt: if (dev->alt != prev_alt) { em28xx_coredbg("minimum isoc packet size: %u (alt=%d)\n", min_pkt_size, dev->alt); diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index db749461e5c61..cc0505eb900f9 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -313,22 +313,20 @@ static int attach_xc3028(u8 addr, struct em28xx *dev) cfg.i2c_addr = addr; if (!dev->dvb->frontend) { - printk(KERN_ERR "%s/2: dvb frontend not attached. " - "Can't attach xc3028\n", - dev->name); + em28xx_errdev("/2: dvb frontend not attached. " + "Can't attach xc3028\n"); return -EINVAL; } fe = dvb_attach(xc2028_attach, dev->dvb->frontend, &cfg); if (!fe) { - printk(KERN_ERR "%s/2: xc3028 attach failed\n", - dev->name); + em28xx_errdev("/2: xc3028 attach failed\n"); dvb_frontend_detach(dev->dvb->frontend); dev->dvb->frontend = NULL; return -EINVAL; } - printk(KERN_INFO "%s/2: xc3028 attached\n", dev->name); + em28xx_info("%s/2: xc3028 attached\n", dev->name); return 0; } @@ -463,7 +461,7 @@ static int dvb_init(struct em28xx *dev) dvb = kzalloc(sizeof(struct em28xx_dvb), GFP_KERNEL); if (dvb == NULL) { - printk(KERN_INFO "em28xx_dvb: memory allocation failed\n"); + em28xx_info("em28xx_dvb: memory allocation failed\n"); return -ENOMEM; } dev->dvb = dvb; @@ -493,6 +491,7 @@ static int dvb_init(struct em28xx *dev) } break; case EM2880_BOARD_HAUPPAUGE_WINTV_HVR_900: + case EM2882_BOARD_TERRATEC_HYBRID_XS: case EM2880_BOARD_EMPIRE_DUAL_TV: dvb->frontend = dvb_attach(zl10353_attach, &em28xx_zl10353_xc3028_no_i2c_gate, @@ -569,15 +568,12 @@ static int dvb_init(struct em28xx *dev) } break; default: - printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card" - " isn't supported yet\n", - dev->name); + em28xx_errdev("/2: The frontend of your DVB/ATSC card" + " isn't supported yet\n"); break; } if (NULL == dvb->frontend) { - printk(KERN_ERR - "%s/2: frontend initialization failed\n", - dev->name); + em28xx_errdev("/2: frontend initialization failed\n"); result = -EINVAL; goto out_free; } @@ -591,7 +587,7 @@ static int dvb_init(struct em28xx *dev) goto out_free; em28xx_set_mode(dev, EM28XX_SUSPEND); - printk(KERN_INFO "Successfully loaded em28xx-dvb\n"); + em28xx_info("Successfully loaded em28xx-dvb\n"); return 0; out_free: diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 7a0fe3816e3db..d96ec7c09dcaa 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -70,6 +70,7 @@ struct em28xx_IR { int polling; struct delayed_work work; unsigned int last_toggle:1; + unsigned int full_code:1; unsigned int last_readcount; unsigned int repeat_interval; @@ -246,9 +247,10 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) return; } - dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n", + dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x%02x\n", poll_result.toggle_bit, poll_result.read_count, - ir->last_readcount, poll_result.rc_data[0]); + ir->last_readcount, poll_result.rc_address, + poll_result.rc_data[0]); if (ir->dev->chip_id == CHIP_ID_EM2874) { /* The em2874 clears the readcount field every time the @@ -282,8 +284,15 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir) if (do_sendkey) { dprintk("sending keypress\n"); - ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0], - poll_result.rc_data[0]); + + if (ir->full_code) + ir_input_keydown(ir->input, &ir->ir, + poll_result.rc_address << 8 | + poll_result.rc_data[0]); + else + ir_input_keydown(ir->input, &ir->ir, + poll_result.rc_data[0]); + ir_input_nokey(ir->input, &ir->ir); } @@ -333,6 +342,8 @@ int em28xx_ir_init(struct em28xx *dev) switch (dev->chip_id) { case CHIP_ID_EM2860: case CHIP_ID_EM2883: + if (dev->model == EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950) + ir->full_code = 1; ir->get_key = default_polling_getkey; break; case CHIP_ID_EM2874: @@ -356,7 +367,11 @@ int em28xx_ir_init(struct em28xx *dev) usb_make_path(dev->udev, ir->phys, sizeof(ir->phys)); strlcat(ir->phys, "/input0", sizeof(ir->phys)); - ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes); + err = ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, + dev->board.ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_USB; @@ -381,6 +396,7 @@ int em28xx_ir_init(struct em28xx *dev) em28xx_ir_stop(ir); dev->ir = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -395,6 +411,7 @@ int em28xx_ir_fini(struct em28xx *dev) return 0; em28xx_ir_stop(ir); + ir_input_free(ir->input); input_unregister_device(ir->input); kfree(ir); diff --git a/drivers/media/video/em28xx/em28xx-reg.h b/drivers/media/video/em28xx/em28xx-reg.h index ed12e7ffcbd0f..058ac87639ce9 100644 --- a/drivers/media/video/em28xx/em28xx-reg.h +++ b/drivers/media/video/em28xx/em28xx-reg.h @@ -192,6 +192,7 @@ /* FIXME: Need to be populated with the other chip ID's */ enum em28xx_chip_id { + CHIP_ID_EM2800 = 7, CHIP_ID_EM2710 = 17, CHIP_ID_EM2820 = 18, /* Also used by some em2710 */ CHIP_ID_EM2840 = 20, diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 3a1dfb7726f8d..7ad65370f274a 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1060,12 +1060,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, /* the em2800 can only scale down to 50% */ height = height > (3 * maxh / 4) ? maxh : maxh / 2; width = width > (3 * maxw / 4) ? maxw : maxw / 2; - /* According to empiatech support the MaxPacketSize is too small - * to support framesizes larger than 640x480 @ 30 fps or 640x576 - * @ 25 fps. As this would cut of a part of the image we prefer - * 360x576 or 360x480 for now */ - if (width == maxw && height == maxh) - width /= 2; } else { /* width must even because of the YUYV format height must be even because of interlacing */ @@ -2225,7 +2219,7 @@ static int em28xx_v4l2_close(struct file *filp) } /* Save some power by putting tuner to sleep */ - v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_standby); + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_power, 0); /* do this before setting alternate! */ em28xx_uninit_isoc(dev); diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 0a73e8bf0d6ef..441df644ddbe3 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -110,6 +110,7 @@ #define EM2820_BOARD_SILVERCREST_WEBCAM 71 #define EM2861_BOARD_GADMEI_UTV330PLUS 72 #define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73 +#define EM2800_BOARD_VC211A 74 /* Limits minimum and default number of buffers */ #define EM28XX_MIN_BUF 4 @@ -143,9 +144,6 @@ */ #define EM28XX_NUM_PACKETS 40 -/* default alternate; 0 means choose the best */ -#define EM28XX_PINOUT 0 - #define EM28XX_INTERLACED_DEFAULT 1 /* @@ -615,7 +613,6 @@ struct em28xx { struct em28xx_dvb *dvb; /* I2C keyboard data */ - struct i2c_board_info info; struct IR_i2c_init_data init_data; }; @@ -800,7 +797,7 @@ static inline unsigned int norm_maxw(struct em28xx *dev) if (dev->board.is_webcam) return dev->sensor_xres; - if (dev->board.max_range_640_480) + if (dev->board.max_range_640_480 || dev->board.is_em2800) return 640; return 720; diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig index fe2e490ebc522..609d65b0b10d4 100644 --- a/drivers/media/video/gspca/Kconfig +++ b/drivers/media/video/gspca/Kconfig @@ -76,10 +76,11 @@ config USB_GSPCA_MR97310A module will be called gspca_mr97310a. config USB_GSPCA_OV519 - tristate "OV519 USB Camera Driver" + tristate "OV51x / OVFX2 / W996xCF USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA help - Say Y here if you want support for cameras based on the OV519 chip. + Say Y here if you want support for cameras based on one of these: + OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF To compile this driver as a module, choose M here: the module will be called gspca_ov519. @@ -103,6 +104,15 @@ config USB_GSPCA_PAC207 To compile this driver as a module, choose M here: the module will be called gspca_pac207. +config USB_GSPCA_PAC7302 + tristate "Pixart PAC7302 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the PAC7302 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_pac7302. + config USB_GSPCA_PAC7311 tristate "Pixart PAC7311 USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA @@ -229,6 +239,15 @@ config USB_GSPCA_STK014 To compile this driver as a module, choose M here: the module will be called gspca_stk014. +config USB_GSPCA_STV0680 + tristate "STV0680 USB Camera Driver" + depends on VIDEO_V4L2 && USB_GSPCA + help + Say Y here if you want support for cameras based on the STV0680 chip. + + To compile this driver as a module, choose M here: the + module will be called gspca_stv0680. + config USB_GSPCA_SUNPLUS tristate "SUNPLUS USB Camera Driver" depends on VIDEO_V4L2 && USB_GSPCA diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index b7420818037e0..ff2c7279d82e6 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o +obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o @@ -22,6 +23,7 @@ obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o +obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o @@ -37,6 +39,7 @@ gspca_mr97310a-objs := mr97310a.o gspca_ov519-objs := ov519.o gspca_ov534-objs := ov534.o gspca_pac207-objs := pac207.o +gspca_pac7302-objs := pac7302.o gspca_pac7311-objs := pac7311.o gspca_sn9c20x-objs := sn9c20x.o gspca_sonixb-objs := sonixb.o @@ -50,6 +53,7 @@ gspca_spca561-objs := spca561.o gspca_sq905-objs := sq905.o gspca_sq905c-objs := sq905c.o gspca_stk014-objs := stk014.o +gspca_stv0680-objs := stv0680.o gspca_sunplus-objs := sunplus.o gspca_t613-objs := t613.o gspca_tv8532-objs := tv8532.o diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index eca003566ae3c..2f0b8d621e003 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -888,8 +888,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -897,16 +896,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - sd->jpeg_hdr, JPEG_HDR_SZ); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); data += 2; len -= 2; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev*gspca_dev) diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c index c1461e63647f2..9de86419ae1ed 100644 --- a/drivers/media/video/gspca/etoms.c +++ b/drivers/media/video/gspca/etoms.c @@ -752,8 +752,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) #undef LIMIT static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { int seqframe; @@ -767,14 +766,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data[2], data[3], data[4], data[5]); data += 30; /* don't change datalength as the chips provided it */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } if (len) { data += 8; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else { /* Drop Packet */ gspca_dev->last_packet_type = DISCARD_PACKET; } diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 480ec5c87d0ed..5d90e7448579c 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -82,7 +82,6 @@ static void dostream(struct work_struct *work) struct gspca_dev *gspca_dev = &dev->gspca_dev; struct urb *urb = gspca_dev->urb[0]; u8 *data = urb->transfer_buffer; - struct gspca_frame *frame; int ret = 0; int len; @@ -118,10 +117,6 @@ static void dostream(struct work_struct *work) } if (!gspca_dev->present || !gspca_dev->streaming) goto out; - frame = gspca_get_i_frame(&dev->gspca_dev); - if (frame == NULL) - gspca_dev->last_packet_type = DISCARD_PACKET; - if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && data[len - 1] == 0xd9)) { @@ -132,21 +127,17 @@ static void dostream(struct work_struct *work) * but there's nothing we can do. We also end * here if the the jpeg ends right at the end * of the frame. */ - if (frame) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - data, len); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, len); break; } /* got a partial image */ - if (frame) - gspca_frame_add(gspca_dev, - gspca_dev->last_packet_type - == LAST_PACKET - ? FIRST_PACKET : INTER_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, + gspca_dev->last_packet_type + == LAST_PACKET + ? FIRST_PACKET : INTER_PACKET, + data, len); } /* We must wait before trying reading the next diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c index 39f6261c1a0ca..1355e526ee845 100644 --- a/drivers/media/video/gspca/gl860/gl860-mi1320.c +++ b/drivers/media/video/gspca/gl860/gl860-mi1320.c @@ -1,6 +1,5 @@ -/* @file gl860-mi1320.c - * @author Olivier LORIN from my logs - * @date 2009-08-27 +/* Subdriver for the GL860 chip with the MI1320 sensor + * Author Olivier LORIN from own logs * * 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 @@ -127,49 +126,49 @@ static u8 dat_wbalBL[] = static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00}; -static u8 s000[] = +static u8 dat_common00[] = "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42" "\xd8\x04\x58\x00\x04\x02"; -static u8 s001[] = +static u8 dat_common01[] = "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d" "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0"; -static u8 s002[] = +static u8 dat_common02[] = "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e" "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00" "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff"; -static u8 s003[] = +static u8 dat_common03[] = "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda" "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c" "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c"; -static u8 s004[] = +static u8 dat_common04[] = "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43"; -static u8 s005[] = +static u8 dat_common05[] = "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68" "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82" "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b"; -static u8 s006[] = +static u8 dat_common06[] = "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06" "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4" "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f"; -static u8 s007[] = +static u8 dat_common07[] = "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72" "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03" "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea" "\xe1\xff\xf1\x00"; -static u8 s008[] = +static u8 dat_common08[] = "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7" "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06" "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a"; -static u8 s009[] = +static u8 dat_common09[] = "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03" "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa" "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14"; -static u8 s010[] = +static u8 dat_common10[] = "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00" "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f" "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01" "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10"; -static u8 s011[] = +static u8 dat_common11[] = "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10" "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00" "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81"; @@ -222,26 +221,26 @@ void mi1320_init_settings(struct gspca_dev *gspca_dev) static void common(struct gspca_dev *gspca_dev) { - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00); ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01); n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common)); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); - ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011); + ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11); keep_on_fetching_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common), n); } diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c index ffb09fed3e8c4..80cb3f1b36f7b 100644 --- a/drivers/media/video/gspca/gl860/gl860-mi2020.c +++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c @@ -1,7 +1,6 @@ -/* @file gl860-mi2020.c - * @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's +/* Subdriver for the GL860 chip with the MI2020 sensor + * Author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist. - * @date 2009-08-27 * * 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 @@ -41,7 +40,7 @@ static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 }; static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 }; static u8 dat_multi6[] = { 0x90, 0x00, 0x05 }; -static struct validx tbl_common_a[] = { +static struct validx tbl_common1[] = { {0x0000, 0x0000}, {1, 0xffff}, /* msleep(35); */ {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, @@ -49,7 +48,7 @@ static struct validx tbl_common_a[] = { {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000}, }; -static struct validx tbl_common_b[] = { +static struct validx tbl_common2[] = { {0x006a, 0x0007}, {35, 0xffff}, {0x00ef, 0x0006}, @@ -60,7 +59,7 @@ static struct validx tbl_common_b[] = { {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000}, }; -static struct idxdata tbl_common_c[] = { +static struct idxdata tbl_common3[] = { {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"}, {6, "\xff\xff\xff"}, /* 12 */ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, @@ -109,7 +108,7 @@ static struct idxdata tbl_common_c[] = { {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"}, }; -static struct idxdata tbl_common_d[] = { +static struct idxdata tbl_common4[] = { {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"}, {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"}, @@ -118,7 +117,7 @@ static struct idxdata tbl_common_d[] = { {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"}, }; -static struct idxdata tbl_common_e[] = { +static struct idxdata tbl_common5[] = { {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"}, @@ -180,7 +179,7 @@ static struct validx tbl_init_at_startup[] = { {53, 0xffff}, }; -static struct idxdata tbl_init_post_alt_low_a[] = { +static struct idxdata tbl_init_post_alt_low1[] = { {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"}, @@ -189,7 +188,7 @@ static struct idxdata tbl_init_post_alt_low_a[] = { {0x33, "\x90\x00\x9b"}, }; -static struct idxdata tbl_init_post_alt_low_b[] = { +static struct idxdata tbl_init_post_alt_low2[] = { {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"}, {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, @@ -197,7 +196,7 @@ static struct idxdata tbl_init_post_alt_low_b[] = { {2, "\xff\xff\xff"}, }; -static struct idxdata tbl_init_post_alt_low_c[] = { +static struct idxdata tbl_init_post_alt_low3[] = { {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"}, {2, "\xff\xff\xff"}, {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"}, @@ -221,7 +220,7 @@ static struct idxdata tbl_init_post_alt_low_c[] = { {1, "\xff\xff\xff"}, }; -static struct idxdata tbl_init_post_alt_low_d[] = { +static struct idxdata tbl_init_post_alt_low4[] = { {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, @@ -267,7 +266,7 @@ static struct idxdata tbl_init_post_alt_low_d[] = { {0x32, "\x6c\x14\x08"}, }; -static struct idxdata tbl_init_post_alt_big_a[] = { +static struct idxdata tbl_init_post_alt_big1[] = { {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"}, {2, "\xff\xff\xff"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"}, @@ -288,7 +287,7 @@ static struct idxdata tbl_init_post_alt_big_a[] = { {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"}, }; -static struct idxdata tbl_init_post_alt_big_b[] = { +static struct idxdata tbl_init_post_alt_big2[] = { {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"}, {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"}, {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"}, @@ -317,7 +316,7 @@ static struct idxdata tbl_init_post_alt_big_b[] = { {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"}, }; -static struct idxdata tbl_init_post_alt_big_c[] = { +static struct idxdata tbl_init_post_alt_big3[] = { {0x33, "\x8c\xa1\x02"}, {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa1\x02"}, @@ -388,14 +387,14 @@ static void common(struct gspca_dev *gspca_dev) s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; if (_MI2020b_) { - fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a)); + fetch_validx(gspca_dev, tbl_common1, ARRAY_SIZE(tbl_common1)); } else { if (_MI2020_) ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL); else ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL); msleep(35); - fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b)); + fetch_validx(gspca_dev, tbl_common2, ARRAY_SIZE(tbl_common2)); } ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01"); ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00"); @@ -403,13 +402,13 @@ static void common(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc"); if (reso == IMAGE_1600) msleep(2); /* 1600 */ - fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c)); + fetch_idxdata(gspca_dev, tbl_common3, ARRAY_SIZE(tbl_common3)); if (_MI2020b_ || _MI2020_) - fetch_idxdata(gspca_dev, tbl_common_d, - ARRAY_SIZE(tbl_common_d)); + fetch_idxdata(gspca_dev, tbl_common4, + ARRAY_SIZE(tbl_common4)); - fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e)); + fetch_idxdata(gspca_dev, tbl_common5, ARRAY_SIZE(tbl_common5)); if (_MI2020b_ || _MI2020_) { /* Different from fret */ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78"); @@ -525,15 +524,15 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) 12, dat_800); if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a, - ARRAY_SIZE(tbl_init_post_alt_low_a)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low1, + ARRAY_SIZE(tbl_init_post_alt_low1)); if (reso == IMAGE_800) - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b, - ARRAY_SIZE(tbl_init_post_alt_low_b)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low2, + ARRAY_SIZE(tbl_init_post_alt_low2)); - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c, - ARRAY_SIZE(tbl_init_post_alt_low_c)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low3, + ARRAY_SIZE(tbl_init_post_alt_low3)); if (_MI2020b_) { ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL); @@ -574,8 +573,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) msleep(5);/* " */ if (_MI2020c_) { - fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d, - ARRAY_SIZE(tbl_init_post_alt_low_d)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_low4, + ARRAY_SIZE(tbl_init_post_alt_low4)); } else { ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c); msleep(14); /* 0xd8 */ @@ -644,8 +643,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) 3, "\x90\x04\xb0"); } - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a, - ARRAY_SIZE(tbl_init_post_alt_big_a)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big1, + ARRAY_SIZE(tbl_init_post_alt_big1)); if (reso == IMAGE_1600) msleep(13); /* 1600 */ @@ -708,8 +707,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) msleep(14); if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b, - ARRAY_SIZE(tbl_init_post_alt_big_b)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big2, + ARRAY_SIZE(tbl_init_post_alt_big2)); /* flip/mirror */ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1); @@ -738,8 +737,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev) sd->nbIm = 0; if (_MI2020c_) - fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c, - ARRAY_SIZE(tbl_init_post_alt_big_c)); + fetch_idxdata(gspca_dev, tbl_init_post_alt_big3, + ARRAY_SIZE(tbl_init_post_alt_big3)); } sd->vold.mirror = mirror; diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/video/gspca/gl860/gl860-ov2640.c index 14b9c373f9f7f..768cac5cd72bb 100644 --- a/drivers/media/video/gspca/gl860/gl860-ov2640.c +++ b/drivers/media/video/gspca/gl860/gl860-ov2640.c @@ -1,6 +1,5 @@ -/* @file gl860-ov2640.c - * @author Olivier LORIN, from Malmostoso's logs - * @date 2009-08-27 +/* Subdriver for the GL860 chip with the OV2640 sensor + * Author Olivier LORIN, from Malmostoso's logs * * 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 @@ -21,8 +20,12 @@ #include "gl860.h" static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01"; -static u8 dat_init2[] = {0x61}; /* expected */ -static u8 dat_init3[] = {0x51}; /* expected */ + +static u8 c61[] = {0x61}; /* expected */ +static u8 c51[] = {0x51}; /* expected */ +static u8 c50[] = {0x50}; /* expected */ +static u8 c28[] = {0x28}; /* expected */ +static u8 ca8[] = {0xa8}; /* expected */ static u8 dat_post[] = "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01"; @@ -32,10 +35,6 @@ static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21"; static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01"; static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41"; -static u8 c50[] = {0x50}; /* expected */ -static u8 c28[] = {0x28}; /* expected */ -static u8 ca8[] = {0xa8}; /* expected */ - static struct validx tbl_init_at_startup[] = { {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1}, {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d}, @@ -92,7 +91,7 @@ static struct validx tbl_common[] = { {0x6000, 0x0010}, }; -static struct validx tbl_sensor_settings_common_a[] = { +static struct validx tbl_sensor_settings_common1[] = { {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2}, {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000}, @@ -104,40 +103,10 @@ static struct validx tbl_sensor_settings_common_a[] = { {0x0040, 0x0000}, }; -static struct validx tbl_sensor_settings_common_b[] = { +static struct validx tbl_sensor_settings_common2[] = { {0x6001, 0x00ff}, {0x6038, 0x000c}, {10, 0xffff}, {0x6000, 0x0011}, - /* backlight=31/64 */ - {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, - /* bright=0/256 */ - {0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d}, - /* wbal=64/128 */ - {0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d}, - /* cntr=0/256 */ - {0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d}, - /* sat=128/256 */ - {0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d}, - /* sharpness=0/32 */ - {0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093}, - /* hue=0/256 */ - {0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d}, - /* gam=32/64 */ - {0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d}, - /* image right up */ - {0xffff, 0xffff}, - {15, 0xffff}, - {0x6001, 0x00ff}, {0x6000, 0x8004}, - {0xffff, 0xffff}, - {0x60a8, 0x0004}, - {15, 0xffff}, - {0x6001, 0x00ff}, {0x6000, 0x8004}, - {0xffff, 0xffff}, - {0x60f8, 0x0004}, - /* image right up */ - {0xffff, 0xffff}, - /* backlight=31/64 */ - {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025}, }; static struct validx tbl_640[] = { @@ -166,7 +135,7 @@ static struct validx tbl_800[] = { {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018}, }; -static struct validx tbl_big_a[] = { +static struct validx tbl_big1[] = { {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045}, {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018}, @@ -176,14 +145,14 @@ static struct validx tbl_big_a[] = { {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c}, }; -static struct validx tbl_big_b[] = { +static struct validx tbl_big2[] = { {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052}, {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057}, {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3}, {0x6000, 0x008e}, }; -static struct validx tbl_big_c[] = { +static struct validx tbl_big3[] = { {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd}, {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0}, {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7}, @@ -223,17 +192,19 @@ void ov2640_init_settings(struct gspca_dev *gspca_dev) sd->vcur.hue = 0; sd->vcur.saturation = 128; sd->vcur.whitebal = 64; + sd->vcur.mirror = 0; + sd->vcur.flip = 0; sd->vmax.backlight = 64; sd->vmax.brightness = 255; sd->vmax.sharpness = 31; sd->vmax.contrast = 255; sd->vmax.gamma = 64; - sd->vmax.hue = 255 + 1; + sd->vmax.hue = 254 + 2; sd->vmax.saturation = 255; sd->vmax.whitebal = 128; - sd->vmax.mirror = 0; - sd->vmax.flip = 0; + sd->vmax.mirror = 1; + sd->vmax.flip = 1; sd->vmax.AC50Hz = 0; sd->dev_camera_settings = ov2640_camera_settings; @@ -259,11 +230,11 @@ static int ov2640_init_at_startup(struct gspca_dev *gspca_dev) common(gspca_dev); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61); ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3); + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51); ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL); /* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */ @@ -275,6 +246,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + sd->mirrorMask = 0; + sd->vold.backlight = -1; sd->vold.brightness = -1; sd->vold.sharpness = -1; @@ -283,6 +256,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) sd->vold.gamma = -1; sd->vold.hue = -1; sd->vold.whitebal = -1; + sd->vold.mirror = -1; + sd->vold.flip = -1; ov2640_init_post_alt(gspca_dev); @@ -292,16 +267,16 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev) static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) { s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL); - n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a, - ARRAY_SIZE(tbl_sensor_settings_common_a)); + n = fetch_validx(gspca_dev, tbl_sensor_settings_common1, + ARRAY_SIZE(tbl_sensor_settings_common1)); ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post); common(gspca_dev); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a, - ARRAY_SIZE(tbl_sensor_settings_common_a), n); + keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1, + ARRAY_SIZE(tbl_sensor_settings_common1), n); switch (reso) { case IMAGE_640: @@ -316,18 +291,18 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) case IMAGE_1600: case IMAGE_1280: - n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a)); + n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1)); if (reso == IMAGE_1280) { - n = fetch_validx(gspca_dev, tbl_big_b, - ARRAY_SIZE(tbl_big_b)); + n = fetch_validx(gspca_dev, tbl_big2, + ARRAY_SIZE(tbl_big2)); } else { ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL); } - n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c)); + n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3)); if (reso == IMAGE_1280) { ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); @@ -343,20 +318,8 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev) break; } - n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b)); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); - ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); - keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b, - ARRAY_SIZE(tbl_sensor_settings_common_b), n); + n = fetch_validx(gspca_dev, tbl_sensor_settings_common2, + ARRAY_SIZE(tbl_sensor_settings_common2)); ov2640_camera_settings(gspca_dev); @@ -393,18 +356,20 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) s32 sat = sd->vcur.saturation; s32 hue = sd->vcur.hue; s32 wbal = sd->vcur.whitebal; + s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0); + s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) == 0); if (backlight != sd->vold.backlight) { + /* No sd->vold.backlight=backlight; (to be done again later) */ if (backlight < 0 || backlight > sd->vmax.backlight) backlight = 0; ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, 0, NULL); - /* No sd->vold.backlight=backlight; (to be done again later) */ } if (bright != sd->vold.brightness) { @@ -466,7 +431,7 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL); ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d, 0, NULL); - if (hue >= sd->vmax.hue) + if (hue >= 255) sd->swapRB = 1; else sd->swapRB = 0; @@ -482,14 +447,33 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev) ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL); } + if (mirror != sd->vold.mirror || flip != sd->vold.flip) { + sd->vold.mirror = mirror; + sd->vold.flip = flip; + + mirror = 0x80 * mirror; + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28); + ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL); + + flip = 0x50 * flip + mirror; + ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL); + ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL); + ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8); + ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL); + + ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50); + } + if (backlight != sd->vold.backlight) { sd->vold.backlight = backlight; ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024, 0, NULL); - ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025, + ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025, 0, NULL); } diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c index eda3346f939cb..d412694c50afc 100644 --- a/drivers/media/video/gspca/gl860/gl860-ov9655.c +++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c @@ -1,7 +1,6 @@ -/* @file gl860-ov9655.c - * @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt +/* Subdriver for the GL860 chip with the OV9655 sensor + * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt * on dsd's weblog - * @date 2009-08-27 * * 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 @@ -104,14 +103,14 @@ static u8 *tbl_800[] = { }; static u8 c04[] = {0x04}; -static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; -static u8 dat_post_2[] = "\x10\x10\xc1\x02"; -static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; -static u8 dat_post_4[] = "\x10\x02\xc1\x06"; -static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; -static u8 dat_post_6[] = "\x10\x10\xc1\x05"; -static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; -static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; +static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02"; +static u8 dat_post2[] = "\x10\x10\xc1\x02"; +static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04"; +static u8 dat_post4[] = "\x10\x02\xc1\x06"; +static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08"; +static u8 dat_post6[] = "\x10\x10\xc1\x05"; +static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08"; +static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09"; static struct validx tbl_init_post_alt[] = { {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff}, @@ -212,7 +211,7 @@ static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev) static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) { s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv; - s32 n; /* reserved for FETCH macros */ + s32 n; /* reserved for FETCH functions */ s32 i; u8 **tbl; @@ -243,7 +242,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); @@ -259,7 +258,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); @@ -270,18 +269,18 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev) keep_on_fetching_validx(gspca_dev, tbl_init_post_alt, ARRAY_SIZE(tbl_init_post_alt), n); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5); - ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7); + ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7); - ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8); + ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8); ov9655_camera_settings(gspca_dev); diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index 6ef59ac7f502c..a695e0ae13c2c 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -1,9 +1,7 @@ -/* @file gl860.c - * @date 2009-08-27 +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver core * - * Genesys Logic webcam with gl860 subdrivers - * - * Driver by Olivier Lorin + * 2009/09/24 Olivier Lorin * GSPCA by Jean-Francois Moine * Thanks BUGabundo and Malmostoso for your amazing help! * @@ -23,8 +21,8 @@ #include "gspca.h" #include "gl860.h" -MODULE_AUTHOR("Olivier Lorin "); -MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver"); +MODULE_AUTHOR("Olivier Lorin "); +MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver"); MODULE_LICENSE("GPL"); /*======================== static function declarations ====================*/ @@ -38,7 +36,7 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev); static int sd_start(struct gspca_dev *gspca_dev); static void sd_stop0(struct gspca_dev *gspca_dev); static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, u8 *data, s32 len); + u8 *data, int len); static void sd_callback(struct gspca_dev *gspca_dev); static int gl860_guess_sensor(struct gspca_dev *gspca_dev, @@ -53,7 +51,7 @@ MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)"); static char sensor[7]; module_param_string(sensor, sensor, sizeof(sensor), 0644); MODULE_PARM_DESC(sensor, - " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')"); + " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')"); /*============================ webcam controls =============================*/ @@ -156,7 +154,7 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev) SET_MY_CTRL(V4L2_CID_VFLIP, V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip) SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY, - V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz) + V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz) return nCtrls; } @@ -435,7 +433,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) /* This function is called when an image is being received */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, u8 *data, s32 len) + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; static s32 nSkipped; @@ -447,11 +445,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* Test only against 0202h, so endianess does not matter */ switch (*(s16 *) data) { case 0x0202: /* End of frame, start a new one */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); nSkipped = 0; if (sd->nbIm >= 0 && sd->nbIm < 10) sd->nbIm++; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); break; default: @@ -466,7 +464,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, nSkipped = nToSkip + 1; } gspca_frame_add(gspca_dev, - INTER_PACKET, frame, data, len); + INTER_PACKET, data, len); } break; } @@ -702,6 +700,7 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL); msleep(56); + PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX"); nOV = 0; for (ntry = 0; ntry < 4; ntry++) { ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL); @@ -711,14 +710,14 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL); msleep(10); ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe); - PDEBUG(D_PROBE, "1st probe=%02x", probe); + PDEBUG(D_PROBE, "probe=0x%02x", probe); if (probe == 0xff) nOV++; } if (nOV) { - PDEBUG(D_PROBE, "0xff -> sensor OVXXXX"); - PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655"); + PDEBUG(D_PROBE, "0xff -> OVXXXX"); + PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655"); nb26 = nb96 = 0; for (ntry = 0; ntry < 4; ntry++) { @@ -728,40 +727,38 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev, ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a, 0, NULL); msleep(10); + /* Wait for 26(OV2640) or 96(OV9655) */ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a, 1, &probe); - PDEBUG(D_PROBE, "2nd probe=%02x", probe); - if (probe == 0x00) - nb26++; if (probe == 0x26 || probe == 0x40) { + PDEBUG(D_PROBE, + "probe=0x%02x -> OV2640", + probe); sd->sensor = ID_OV2640; nb26 += 4; break; } if (probe == 0x96 || probe == 0x55) { + PDEBUG(D_PROBE, + "probe=0x%02x -> OV9655", + probe); sd->sensor = ID_OV9655; nb96 += 4; break; } + PDEBUG(D_PROBE, "probe=0x%02x", probe); + if (probe == 0x00) + nb26++; if (probe == 0xff) nb96++; msleep(3); } - if (nb26 < 4 && nb96 < 4) { - PDEBUG(D_PROBE, "No relevant answer "); - PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655"); - PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640"); - PDEBUG(D_PROBE, - "To force a sensor, add that line to " - "/etc/modprobe.d/options.conf:"); - PDEBUG(D_PROBE, "options gspca_gl860 " - "sensor=\"OV2640\" or \"OV9655\""); + if (nb26 < 4 && nb96 < 4) return -1; - } - } else { /* probe = 0 */ - PDEBUG(D_PROBE, "No 0xff -> sensor MI2020"); + } else { + PDEBUG(D_PROBE, "Not any 0xff -> MI2020"); sd->sensor = ID_MI2020; } } diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h index cef4e24c1e61d..305061ff8387f 100644 --- a/drivers/media/video/gspca/gl860/gl860.h +++ b/drivers/media/video/gspca/gl860/gl860.h @@ -1,6 +1,7 @@ -/* @file gl860.h - * @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN - * @date 2009-08-27 +/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip + * Subdriver declarations + * + * 2009/10/14 Olivier LORIN * * 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 diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index 23d3fb7769186..4076f8e5a6fc4 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine "); MODULE_DESCRIPTION("GSPCA USB Camera Driver"); MODULE_LICENSE("GPL"); -#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 7, 0) +#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 8, 0) #ifdef GSPCA_DEBUG int gspca_debug = D_ERR | D_PROBE; @@ -74,7 +74,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h) #define PDEBUG_MODE(txt, pixfmt, w, h) #endif -/* specific memory types - !! should different from V4L2_MEMORY_xxx */ +/* specific memory types - !! should be different from V4L2_MEMORY_xxx */ #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ #define GSPCA_MEMORY_READ 7 @@ -126,7 +126,6 @@ EXPORT_SYMBOL(gspca_get_i_frame); static void fill_frame(struct gspca_dev *gspca_dev, struct urb *urb) { - struct gspca_frame *frame; u8 *data; /* address of data in the iso message */ int i, len, st; cam_pkt_op pkt_scan; @@ -135,21 +134,16 @@ static void fill_frame(struct gspca_dev *gspca_dev, if (urb->status == -ESHUTDOWN) return; /* disconnection */ #ifdef CONFIG_PM - if (!gspca_dev->frozen) + if (gspca_dev->frozen) + return; #endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - return; + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + urb->status = 0; + goto resubmit; } pkt_scan = gspca_dev->sd_desc->pkt_scan; for (i = 0; i < urb->number_of_packets; i++) { - /* check the availability of the frame buffer */ - frame = gspca_get_i_frame(gspca_dev); - if (!frame) { - gspca_dev->last_packet_type = DISCARD_PACKET; - break; - } - /* check the packet status and length */ len = urb->iso_frame_desc[i].actual_length; if (len == 0) { @@ -171,9 +165,10 @@ static void fill_frame(struct gspca_dev *gspca_dev, i, urb->iso_frame_desc[i].offset, len); data = (u8 *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; - pkt_scan(gspca_dev, frame, data, len); + pkt_scan(gspca_dev, data, len); } +resubmit: /* resubmit the URB */ st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) @@ -201,7 +196,6 @@ static void isoc_irq(struct urb *urb) static void bulk_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; - struct gspca_frame *frame; int st; PDEBUG(D_PACK, "bulk irq"); @@ -212,29 +206,22 @@ static void bulk_irq(struct urb *urb) break; case -ESHUTDOWN: return; /* disconnection */ - case -ECONNRESET: - urb->status = 0; - break; default: #ifdef CONFIG_PM - if (!gspca_dev->frozen) + if (gspca_dev->frozen) + return; #endif - PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); - return; + PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status); + urb->status = 0; + goto resubmit; } - /* check the availability of the frame buffer */ - frame = gspca_get_i_frame(gspca_dev); - if (!frame) { - gspca_dev->last_packet_type = DISCARD_PACKET; - } else { - PDEBUG(D_PACK, "packet l:%d", urb->actual_length); - gspca_dev->sd_desc->pkt_scan(gspca_dev, - frame, - urb->transfer_buffer, - urb->actual_length); - } + PDEBUG(D_PACK, "packet l:%d", urb->actual_length); + gspca_dev->sd_desc->pkt_scan(gspca_dev, + urb->transfer_buffer, + urb->actual_length); +resubmit: /* resubmit the URB */ if (gspca_dev->cam.bulk_nurbs != 0) { st = usb_submit_urb(urb, GFP_ATOMIC); @@ -255,24 +242,27 @@ static void bulk_irq(struct urb *urb) * DISCARD_PACKET invalidates the whole frame. * On LAST_PACKET, a new frame is returned. */ -struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, - enum gspca_packet_type packet_type, - struct gspca_frame *frame, - const __u8 *data, - int len) +void gspca_frame_add(struct gspca_dev *gspca_dev, + enum gspca_packet_type packet_type, + const u8 *data, + int len) { + struct gspca_frame *frame; int i, j; PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); + /* check the availability of the frame buffer */ + frame = gspca_dev->cur_frame; + if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) + != V4L2_BUF_FLAG_QUEUED) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + /* when start of a new frame, if the current frame buffer * is not queued, discard the whole frame */ if (packet_type == FIRST_PACKET) { - if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS) - != V4L2_BUF_FLAG_QUEUED) { - gspca_dev->last_packet_type = DISCARD_PACKET; - return frame; - } frame->data_end = frame->data; jiffies_to_timeval(get_jiffies_64(), &frame->v4l2_buf.timestamp); @@ -280,7 +270,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, } else if (gspca_dev->last_packet_type == DISCARD_PACKET) { if (packet_type == LAST_PACKET) gspca_dev->last_packet_type = packet_type; - return frame; + return; } /* append the packet to the frame buffer */ @@ -312,9 +302,9 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, i, gspca_dev->fr_o); j = gspca_dev->fr_queue[i]; - frame = &gspca_dev->frame[j]; + gspca_dev->cur_frame = &gspca_dev->frame[j]; } - return frame; + return; } EXPORT_SYMBOL(gspca_frame_add); @@ -395,6 +385,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev, frame->v4l2_buf.m.offset = i * frsz; } gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0; + gspca_dev->cur_frame = &gspca_dev->frame[0]; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; return 0; @@ -475,10 +466,18 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev) xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK : USB_ENDPOINT_XFER_ISOC; i = gspca_dev->alt; /* previous alt setting */ - while (--i >= 0) { - ep = alt_xfer(&intf->altsetting[i], xfer); - if (ep) - break; + if (gspca_dev->cam.reverse_alts) { + while (++i < gspca_dev->nbalt) { + ep = alt_xfer(&intf->altsetting[i], xfer); + if (ep) + break; + } + } else { + while (--i >= 0) { + ep = alt_xfer(&intf->altsetting[i], xfer); + if (ep) + break; + } } if (ep == NULL) { err("no transfer endpoint found"); @@ -599,7 +598,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) /* set the higher alternate setting and * loop until urb submit succeeds */ - gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->cam.reverse_alts) + gspca_dev->alt = 0; + else + gspca_dev->alt = gspca_dev->nbalt; + if (gspca_dev->sd_desc->isoc_init) { ret = gspca_dev->sd_desc->isoc_init(gspca_dev); if (ret < 0) @@ -641,15 +644,19 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) } if (ret >= 0) break; - PDEBUG(D_ERR|D_STREAM, - "usb_submit_urb alt %d err %d", gspca_dev->alt, ret); gspca_dev->streaming = 0; destroy_urbs(gspca_dev); - if (ret != -ENOSPC) + if (ret != -ENOSPC) { + PDEBUG(D_ERR|D_STREAM, + "usb_submit_urb alt %d err %d", + gspca_dev->alt, ret); goto out; + } /* the bandwidth is not wide enough * negociate or try a lower alternate setting */ + PDEBUG(D_ERR|D_STREAM, + "bandwidth not wide enough - trying again"); msleep(20); /* wait for kill complete */ if (gspca_dev->sd_desc->isoc_nego) { ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); @@ -980,7 +987,7 @@ static void gspca_release(struct video_device *vfd) { struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); - PDEBUG(D_STREAM, "device released"); + PDEBUG(D_PROBE, "/dev/video%d released", gspca_dev->vdev.num); kfree(gspca_dev->usb_buf); kfree(gspca_dev); @@ -991,7 +998,7 @@ static int dev_open(struct file *file) struct gspca_dev *gspca_dev; int ret; - PDEBUG(D_STREAM, "%s open", current->comm); + PDEBUG(D_STREAM, "[%s] open", current->comm); gspca_dev = (struct gspca_dev *) video_devdata(file); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; @@ -1037,7 +1044,7 @@ static int dev_close(struct file *file) { struct gspca_dev *gspca_dev = file->private_data; - PDEBUG(D_STREAM, "%s close", current->comm); + PDEBUG(D_STREAM, "[%s] close", current->comm); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; gspca_dev->users--; @@ -1138,10 +1145,13 @@ static int vidioc_queryctrl(struct file *file, void *priv, } } else { ctrls = get_ctrl(gspca_dev, id); + i = ctrls - gspca_dev->sd_desc->ctrls; } if (ctrls == NULL) return -EINVAL; memcpy(q_ctrl, ctrls, sizeof *q_ctrl); + if (gspca_dev->ctrl_inac & (1 << i)) + q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; return 0; } @@ -1603,7 +1613,7 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) size -= PAGE_SIZE; } - vma->vm_ops = &gspca_vm_ops; + vma->vm_ops = (struct vm_operations_struct *) &gspca_vm_ops; vma->vm_private_data = frame; gspca_vm_open(vma); ret = 0; @@ -2001,11 +2011,15 @@ int gspca_dev_probe(struct usb_interface *intf, PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); /* we don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) + if (dev->descriptor.bNumConfigurations != 1) { + PDEBUG(D_ERR, "Too many config"); return -ENODEV; + } interface = &intf->cur_altsetting->desc; - if (interface->bInterfaceNumber > 0) + if (interface->bInterfaceNumber > 0) { + PDEBUG(D_ERR, "intf != 0"); return -ENODEV; + } /* create the device */ if (dev_size < sizeof *gspca_dev) @@ -2059,7 +2073,7 @@ int gspca_dev_probe(struct usb_interface *intf, } usb_set_intfdata(intf, gspca_dev); - PDEBUG(D_PROBE, "probe ok"); + PDEBUG(D_PROBE, "/dev/video%d created", gspca_dev->vdev.num); return 0; out: kfree(gspca_dev->usb_buf); @@ -2078,6 +2092,7 @@ void gspca_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + PDEBUG(D_PROBE, "/dev/video%d disconnect", gspca_dev->vdev.num); mutex_lock(&gspca_dev->usb_lock); gspca_dev->present = 0; @@ -2096,7 +2111,7 @@ void gspca_disconnect(struct usb_interface *intf) /* (this will call gspca_release() immediatly or on last close) */ video_unregister_device(&gspca_dev->vdev); - PDEBUG(D_PROBE, "disconnect complete"); +/* PDEBUG(D_PROBE, "disconnect complete"); */ } EXPORT_SYMBOL(gspca_disconnect); diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 70b1fd830876a..181617355ec30 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -58,6 +58,7 @@ struct cam { u8 npkt; /* number of packets in an ISOC message * 0 is the default value: 32 packets */ u32 input_flags; /* value for ENUM_INPUT status flags */ + char reverse_alts; /* Alt settings are in high to low order */ }; struct gspca_dev; @@ -78,8 +79,7 @@ typedef int (*cam_streamparm_op) (struct gspca_dev *, typedef int (*cam_qmnu_op) (struct gspca_dev *, struct v4l2_querymenu *); typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len); struct ctrl { @@ -142,6 +142,7 @@ struct gspca_dev { struct cam cam; /* device information */ const struct sd_desc *sd_desc; /* subdriver description */ unsigned ctrl_dis; /* disabled controls (bit map) */ + unsigned ctrl_inac; /* inactive controls (bit map) */ #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ @@ -149,6 +150,7 @@ struct gspca_dev { __u8 *frbuf; /* buffer for nframes */ struct gspca_frame frame[GSPCA_MAX_FRAMES]; + struct gspca_frame *cur_frame; /* frame beeing filled */ __u32 frsz; /* frame size */ char nframes; /* number of frames */ char fr_i; /* frame being filled */ @@ -189,11 +191,10 @@ int gspca_dev_probe(struct usb_interface *intf, int dev_size, struct module *module); void gspca_disconnect(struct usb_interface *intf); -struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev, - enum gspca_packet_type packet_type, - struct gspca_frame *frame, - const __u8 *data, - int len); +void gspca_frame_add(struct gspca_dev *gspca_dev, + enum gspca_packet_type packet_type, + const u8 *data, + int len); struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev); #ifdef CONFIG_PM int gspca_suspend(struct usb_interface *intf, pm_message_t message); diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c index a11c97ebeb0f1..2019b04f92352 100644 --- a/drivers/media/video/gspca/jeilinj.c +++ b/drivers/media/video/gspca/jeilinj.c @@ -181,11 +181,9 @@ static void jlj_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int blocks_left; /* 0x200-sized blocks remaining in current frame. */ int size_in_blocks; int act_len; - int discarding = 0; /* true if we failed to get space for frame. */ int packet_type; int ret; u8 *buffer; @@ -196,15 +194,6 @@ static void jlj_dostream(struct work_struct *work) goto quit_stream; } while (gspca_dev->present && gspca_dev->streaming) { - if (!gspca_dev->present) - goto quit_stream; - /* Start a new frame, and add the JPEG header, first thing */ - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - dev->jpeg_hdr, JPEG_HDR_SZ); - else - discarding = 1; /* * Now request data block 0. Line 0 reports the size * to download, in blocks of size 0x200, and also tells the @@ -222,14 +211,15 @@ static void jlj_dostream(struct work_struct *work) size_in_blocks = buffer[0x0a]; blocks_left = buffer[0x0a] - 1; PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left); - packet_type = INTER_PACKET; - if (frame && !discarding) - /* Toss line 0 of data block 0, keep the rest. */ - gspca_frame_add(gspca_dev, packet_type, - frame, buffer + FRAME_HEADER_LEN, + + /* Start a new frame, and add the JPEG header, first thing */ + gspca_frame_add(gspca_dev, FIRST_PACKET, + dev->jpeg_hdr, JPEG_HDR_SZ); + /* Toss line 0 of data block 0, keep the rest. */ + gspca_frame_add(gspca_dev, INTER_PACKET, + buffer + FRAME_HEADER_LEN, JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); - else - discarding = 1; + while (blocks_left > 0) { if (!gspca_dev->present) goto quit_stream; @@ -246,12 +236,8 @@ static void jlj_dostream(struct work_struct *work) packet_type = LAST_PACKET; else packet_type = INTER_PACKET; - if (frame && !discarding) - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, - JEILINJ_MAX_TRANSFER); - else - discarding = 1; + gspca_frame_add(gspca_dev, packet_type, + buffer, JEILINJ_MAX_TRANSFER); } } quit_stream: diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c index 7f1e5415850b5..844fc1d886d19 100644 --- a/drivers/media/video/gspca/m5602/m5602_core.c +++ b/drivers/media/video/gspca/m5602/m5602_core.c @@ -274,8 +274,7 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev) } static void m5602_urb_complete(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, int len) + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; @@ -295,19 +294,27 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, len -= 6; /* Complete the last frame (if any) */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); sd->frame_count++; /* Create a new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); PDEBUG(D_FRAM, "Starting new frame %d", sd->frame_count); } else { - int cur_frame_len = frame->data_end - frame->data; + struct gspca_frame *frame; + int cur_frame_len; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + cur_frame_len = frame->data_end - frame->data; /* Remove urb header */ data += 4; len -= 4; @@ -316,12 +323,12 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev, PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes", sd->frame_count, len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else if (frame->v4l2_buf.length - cur_frame_len > 0) { /* Add the remaining data up to frame size */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, - frame->v4l2_buf.length - cur_frame_len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, + frame->v4l2_buf.length - cur_frame_len); } } } diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index de769caf013dd..9cf8d68c71bf9 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -325,8 +325,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -348,11 +347,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, || data[5 + p] == 0x67) { PDEBUG(D_PACK, "sof offset: %d len: %d", p, len); - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, p); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, p); /* put the JPEG header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); data += p + 16; len -= p + 16; @@ -360,7 +359,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } } } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c index f8328b9efae53..126d968dd9e00 100644 --- a/drivers/media/video/gspca/mr97310a.c +++ b/drivers/media/video/gspca/mr97310a.c @@ -1,23 +1,30 @@ /* * Mars MR97310A library * + * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is * Copyright (C) 2009 Kyle Guinn * * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+ * and for the routines for detecting and classifying these various cameras, + * is Copyright (C) 2009 Theodore Kilgore * + * Support for the control settings for the CIF cameras is + * Copyright (C) 2009 Hans de Goede and + * Thomas Kaiser + * + * Support for the control settings for the VGA cameras is * Copyright (C) 2009 Theodore Kilgore * - * Acknowledgements: + * Several previously unsupported cameras are owned and have been tested by + * Hans de Goede and + * Thomas Kaiser and + * Theodore Kilgore and + * Edmond Rodriguez and + * Aurelien Jacobs * * The MR97311A support in gspca/mars.c has been helpful in understanding some * of the registers in these cameras. * - * Hans de Goede and - * Thomas Kaiser - * have assisted with their experience. Each of them has also helped by - * testing a previously unsupported camera. - * * 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, or @@ -40,11 +47,9 @@ #define CAM_TYPE_CIF 0 #define CAM_TYPE_VGA 1 -#define MR97310A_BRIGHTNESS_MIN -254 -#define MR97310A_BRIGHTNESS_MAX 255 #define MR97310A_BRIGHTNESS_DEFAULT 0 -#define MR97310A_EXPOSURE_MIN 300 +#define MR97310A_EXPOSURE_MIN 0 #define MR97310A_EXPOSURE_MAX 4095 #define MR97310A_EXPOSURE_DEFAULT 1000 @@ -52,6 +57,10 @@ #define MR97310A_GAIN_MAX 31 #define MR97310A_GAIN_DEFAULT 25 +#define MR97310A_MIN_CLOCKDIV_MIN 3 +#define MR97310A_MIN_CLOCKDIV_MAX 8 +#define MR97310A_MIN_CLOCKDIV_DEFAULT 3 + MODULE_AUTHOR("Kyle Guinn ," "Theodore Kilgore "); MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver"); @@ -69,10 +78,12 @@ struct sd { u8 cam_type; /* 0 is CIF and 1 is VGA */ u8 sensor_type; /* We use 0 and 1 here, too. */ u8 do_lcd_stop; + u8 adj_colors; int brightness; u16 exposure; u8 gain; + u8 min_clockdiv; }; struct sensor_w_data { @@ -82,26 +93,31 @@ struct sensor_w_data { int len; }; +static void sd_stopN(struct gspca_dev *gspca_dev); static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val); static void setbrightness(struct gspca_dev *gspca_dev); static void setexposure(struct gspca_dev *gspca_dev); static void setgain(struct gspca_dev *gspca_dev); /* V4L2 controls supported by the driver */ static struct ctrl sd_ctrls[] = { +/* Separate brightness control description for Argus QuickClix as it has + different limits from the other mr97310a cameras */ { -#define BRIGHTNESS_IDX 0 +#define NORM_BRIGHTNESS_IDX 0 { .id = V4L2_CID_BRIGHTNESS, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Brightness", - .minimum = MR97310A_BRIGHTNESS_MIN, - .maximum = MR97310A_BRIGHTNESS_MAX, + .minimum = -254, + .maximum = 255, .step = 1, .default_value = MR97310A_BRIGHTNESS_DEFAULT, .flags = 0, @@ -110,7 +126,22 @@ static struct ctrl sd_ctrls[] = { .get = sd_getbrightness, }, { -#define EXPOSURE_IDX 1 +#define ARGUS_QC_BRIGHTNESS_IDX 1 + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 15, + .step = 1, + .default_value = MR97310A_BRIGHTNESS_DEFAULT, + .flags = 0, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { +#define EXPOSURE_IDX 2 { .id = V4L2_CID_EXPOSURE, .type = V4L2_CTRL_TYPE_INTEGER, @@ -125,7 +156,7 @@ static struct ctrl sd_ctrls[] = { .get = sd_getexposure, }, { -#define GAIN_IDX 2 +#define GAIN_IDX 3 { .id = V4L2_CID_GAIN, .type = V4L2_CTRL_TYPE_INTEGER, @@ -139,6 +170,21 @@ static struct ctrl sd_ctrls[] = { .set = sd_setgain, .get = sd_getgain, }, + { +#define MIN_CLOCKDIV_IDX 4 + { + .id = V4L2_CID_PRIVATE_BASE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Minimum Clock Divider", + .minimum = MR97310A_MIN_CLOCKDIV_MIN, + .maximum = MR97310A_MIN_CLOCKDIV_MAX, + .step = 1, + .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT, + .flags = 0, + }, + .set = sd_setmin_clockdiv, + .get = sd_getmin_clockdiv, + }, }; static const struct v4l2_pix_format vga_mode[] = { @@ -230,12 +276,17 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) int rc; buf = data; - rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + if (sd->cam_type == CAM_TYPE_CIF) { + rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1); + confirm_reg = sd->sensor_type ? 0x13 : 0x11; + } else { + rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1); + confirm_reg = 0x11; + } if (rc < 0) return rc; buf = 0x01; - confirm_reg = sd->sensor_type ? 0x13 : 0x11; rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1); if (rc < 0) return rc; @@ -243,18 +294,26 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data) return 0; } -static int cam_get_response16(struct gspca_dev *gspca_dev) +static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose) { - __u8 *data = gspca_dev->usb_buf; int err_code; - data[0] = 0x21; + gspca_dev->usb_buf[0] = reg; err_code = mr_write(gspca_dev, 1); if (err_code < 0) return err_code; err_code = mr_read(gspca_dev, 16); - return err_code; + if (err_code < 0) + return err_code; + + if (verbose) + PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg, + gspca_dev->usb_buf[0], + gspca_dev->usb_buf[1], + gspca_dev->usb_buf[2]); + + return 0; } static int zero_the_pointer(struct gspca_dev *gspca_dev) @@ -264,7 +323,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) u8 status = 0; int tries = 0; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -275,7 +334,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -285,7 +344,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -295,7 +354,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); if (err_code < 0) return err_code; @@ -306,7 +365,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) return err_code; while (status != 0x0a && tries < 256) { - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); status = data[0]; tries++; if (err_code < 0) @@ -323,7 +382,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) if (err_code < 0) return err_code; - err_code = cam_get_response16(gspca_dev); + err_code = cam_get_response16(gspca_dev, 0x21, 0); status = data[0]; tries++; if (err_code < 0) @@ -342,89 +401,202 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev) return 0; } -static u8 get_sensor_id(struct gspca_dev *gspca_dev) +static int stream_start(struct gspca_dev *gspca_dev) { - int err_code; - - gspca_dev->usb_buf[0] = 0x1e; - err_code = mr_write(gspca_dev, 1); - if (err_code < 0) - return err_code; + gspca_dev->usb_buf[0] = 0x01; + gspca_dev->usb_buf[1] = 0x01; + return mr_write(gspca_dev, 2); +} - err_code = mr_read(gspca_dev, 16); - if (err_code < 0) - return err_code; +static void stream_stop(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x01; + gspca_dev->usb_buf[1] = 0x00; + if (mr_write(gspca_dev, 2) < 0) + PDEBUG(D_ERR, "Stream Stop failed"); +} - PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[0]); +static void lcd_stop(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x19; + gspca_dev->usb_buf[1] = 0x54; + if (mr_write(gspca_dev, 2) < 0) + PDEBUG(D_ERR, "LCD Stop failed"); +} - return gspca_dev->usb_buf[0]; +static int isoc_enable(struct gspca_dev *gspca_dev) +{ + gspca_dev->usb_buf[0] = 0x00; + gspca_dev->usb_buf[1] = 0x4d; /* ISOC transfering enable... */ + return mr_write(gspca_dev, 2); } -/* this function is called at probe time */ +/* This function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; struct cam *cam; - __u8 *data = gspca_dev->usb_buf; int err_code; cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + sd->do_lcd_stop = 0; + + /* Several of the supported CIF cameras share the same USB ID but + * require different initializations and different control settings. + * The same is true of the VGA cameras. Therefore, we are forced + * to start the initialization process in order to determine which + * camera is present. Some of the supported cameras require the + * memory pointer to be set to 0 as the very first item of business + * or else they will not stream. So we do that immediately. + */ + err_code = zero_the_pointer(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = stream_start(gspca_dev); + if (err_code < 0) + return err_code; - if (id->idProduct == 0x010e) { + if (id->idProduct == 0x0110 || id->idProduct == 0x010e) { sd->cam_type = CAM_TYPE_CIF; cam->nmodes--; - - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); + err_code = cam_get_response16(gspca_dev, 0x06, 1); if (err_code < 0) return err_code; - - msleep(200); - data[0] = get_sensor_id(gspca_dev); /* - * Known CIF cameras. If you have another to report, please do + * All but one of the known CIF cameras share the same USB ID, + * but two different init routines are in use, and the control + * settings are different, too. We need to detect which camera + * of the two known varieties is connected! * - * Name byte just read sd->sensor_type - * reported by - * Sakar Spy-shot 0x28 T. Kilgore 0 - * Innovage 0xf5 (unstable) T. Kilgore 0 - * Vivitar Mini 0x53 H. De Goede 0 - * Vivitar Mini 0x04 / 0x24 E. Rodriguez 0 - * Vivitar Mini 0x08 T. Kilgore 1 - * Elta-Media 8212dc 0x23 T. Kaiser 1 - * Philips dig. keych. 0x37 T. Kilgore 1 + * A list of known CIF cameras follows. They all report either + * 0002 for type 0 or 0003 for type 1. + * If you have another to report, please do + * + * Name sd->sensor_type reported by + * + * Sakar Spy-shot 0 T. Kilgore + * Innovage 0 T. Kilgore + * Vivitar Mini 0 H. De Goede + * Vivitar Mini 0 E. Rodriguez + * Vivitar Mini 1 T. Kilgore + * Elta-Media 8212dc 1 T. Kaiser + * Philips dig. keych. 1 T. Kilgore + * Trust Spyc@m 100 1 A. Jacobs */ - if ((data[0] & 0x78) == 8 || - ((data[0] & 0x2) == 0x2 && data[0] != 0x53)) - sd->sensor_type = 1; - else + switch (gspca_dev->usb_buf[1]) { + case 2: sd->sensor_type = 0; - + break; + case 3: + sd->sensor_type = 1; + break; + default: + PDEBUG(D_ERR, "Unknown CIF Sensor id : %02x", + gspca_dev->usb_buf[1]); + return -ENODEV; + } PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d", sd->sensor_type); + } else { + sd->cam_type = CAM_TYPE_VGA; - if (force_sensor_type != -1) { - sd->sensor_type = !! force_sensor_type; - PDEBUG(D_PROBE, "Forcing sensor type to: %d", - sd->sensor_type); + err_code = cam_get_response16(gspca_dev, 0x07, 1); + if (err_code < 0) + return err_code; + + /* + * Here is a table of the responses to the previous command + * from the known MR97310A VGA cameras. + * + * Name gspca_dev->usb_buf[] sd->sensor_type + * sd->do_lcd_stop + * Aiptek Pencam VGA+ 0300 0 1 + * ION digital 0350 0 1 + * Argus DC-1620 0450 1 0 + * Argus QuickClix 0420 1 1 + * + * Based upon these results, we assume default settings + * and then correct as necessary, as follows. + * + */ + + sd->sensor_type = 1; + sd->do_lcd_stop = 0; + sd->adj_colors = 0; + if ((gspca_dev->usb_buf[0] != 0x03) && + (gspca_dev->usb_buf[0] != 0x04)) { + PDEBUG(D_ERR, "Unknown VGA Sensor id Byte 0: %02x", + gspca_dev->usb_buf[1]); + PDEBUG(D_ERR, "Defaults assumed, may not work"); + PDEBUG(D_ERR, "Please report this"); + } + /* Sakar Digital color needs to be adjusted. */ + if ((gspca_dev->usb_buf[0] == 0x03) && + (gspca_dev->usb_buf[1] == 0x50)) + sd->adj_colors = 1; + if (gspca_dev->usb_buf[0] == 0x04) { + sd->do_lcd_stop = 1; + switch (gspca_dev->usb_buf[1]) { + case 0x50: + sd->sensor_type = 0; + PDEBUG(D_PROBE, "sensor_type corrected to 0"); + break; + case 0x20: + /* Nothing to do here. */ + break; + default: + PDEBUG(D_ERR, + "Unknown VGA Sensor id Byte 1: %02x", + gspca_dev->usb_buf[1]); + PDEBUG(D_ERR, + "Defaults assumed, may not work"); + PDEBUG(D_ERR, "Please report this"); + } } + PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d", + sd->sensor_type); + } + /* Stop streaming as we've started it to probe the sensor type. */ + sd_stopN(gspca_dev); + + if (force_sensor_type != -1) { + sd->sensor_type = !!force_sensor_type; + PDEBUG(D_PROBE, "Forcing sensor type to: %d", + sd->sensor_type); + } + /* Setup controls depending on camera type */ + if (sd->cam_type == CAM_TYPE_CIF) { + /* No brightness for sensor_type 0 */ if (sd->sensor_type == 0) - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX); + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | + (1 << ARGUS_QC_BRIGHTNESS_IDX); + else + gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << MIN_CLOCKDIV_IDX); } else { - sd->cam_type = CAM_TYPE_VGA; - PDEBUG(D_PROBE, "MR97310A VGA camera detected"); - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) | - (1 << EXPOSURE_IDX) | (1 << GAIN_IDX); + /* All controls need to be disabled if VGA sensor_type is 0 */ + if (sd->sensor_type == 0) + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) | + (1 << ARGUS_QC_BRIGHTNESS_IDX) | + (1 << EXPOSURE_IDX) | + (1 << GAIN_IDX) | + (1 << MIN_CLOCKDIV_IDX); + else if (sd->do_lcd_stop) + /* Argus QuickClix has different brightness limits */ + gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX); + else + gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX); } sd->brightness = MR97310A_BRIGHTNESS_DEFAULT; sd->exposure = MR97310A_EXPOSURE_DEFAULT; sd->gain = MR97310A_GAIN_DEFAULT; + sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT; return 0; } @@ -455,11 +627,6 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) }; /* Note: Some of the above descriptions guessed from MR97113A driver */ - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; memcpy(data, startup_string, 11); if (sd->sensor_type) @@ -533,22 +700,7 @@ static int start_cif_cam(struct gspca_dev *gspca_dev) err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data, ARRAY_SIZE(cif_sensor1_init_data)); } - if (err_code < 0) - return err_code; - - setbrightness(gspca_dev); - setexposure(gspca_dev); - setgain(gspca_dev); - - msleep(200); - - data[0] = 0x00; - data[1] = 0x4d; /* ISOC transfering enable... */ - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - - return 0; + return err_code; } static int start_vga_cam(struct gspca_dev *gspca_dev) @@ -558,84 +710,8 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) int err_code; const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x50, 0xc0}; - /* What some of these mean is explained in start_cif_cam(), above */ - sd->sof_read = 0; - - /* - * We have to know which camera we have, because the register writes - * depend upon the camera. This test, run before we actually enter - * the initialization routine, distinguishes most of the cameras, If - * needed, another routine is done later, too. - */ - memset(data, 0, 16); - data[0] = 0x20; - err_code = mr_write(gspca_dev, 1); - if (err_code < 0) - return err_code; - - err_code = mr_read(gspca_dev, 16); - if (err_code < 0) - return err_code; - - PDEBUG(D_PROBE, "Byte reported is %02x", data[0]); - - msleep(200); - /* - * Known VGA cameras. If you have another to report, please do - * - * Name byte just read sd->sensor_type - * sd->do_lcd_stop - * Aiptek Pencam VGA+ 0x31 0 1 - * ION digital 0x31 0 1 - * Argus DC-1620 0x30 1 0 - * Argus QuickClix 0x30 1 1 (not caught here) - */ - sd->sensor_type = data[0] & 1; - sd->do_lcd_stop = (~data[0]) & 1; - - - - /* Streaming setup begins here. */ - - - data[0] = 0x01; - data[1] = 0x01; - err_code = mr_write(gspca_dev, 2); - if (err_code < 0) - return err_code; - /* - * A second test can now resolve any remaining ambiguity in the - * identification of the camera type, - */ - if (!sd->sensor_type) { - data[0] = get_sensor_id(gspca_dev); - if (data[0] == 0x7f) { - sd->sensor_type = 1; - PDEBUG(D_PROBE, "sensor_type corrected to 1"); - } - msleep(200); - } - - if (force_sensor_type != -1) { - sd->sensor_type = !! force_sensor_type; - PDEBUG(D_PROBE, "Forcing sensor type to: %d", - sd->sensor_type); - } - - /* - * Known VGA cameras. - * This test is only run if the previous test returned 0x30, but - * here is the information for all others, too, just for reference. - * - * Name byte just read sd->sensor_type - * - * Aiptek Pencam VGA+ 0xfb (this test not run) 1 - * ION digital 0xbd (this test not run) 1 - * Argus DC-1620 0xe5 (no change) 0 - * Argus QuickClix 0x7f (reclassified) 1 - */ memcpy(data, startup_string, 11); if (!sd->sensor_type) { data[5] = 0x00; @@ -689,29 +765,44 @@ static int start_vga_cam(struct gspca_dev *gspca_dev) err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data, ARRAY_SIZE(vga_sensor0_init_data)); } else { /* sd->sensor_type = 1 */ - const struct sensor_w_data vga_sensor1_init_data[] = { + const struct sensor_w_data color_adj[] = { {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, - 0x07, 0x00, 0x01}, 8}, + /* adjusted blue, green, red gain correct + too much blue from the Sakar Digital */ + 0x05, 0x01, 0x04}, 8} + }; + + const struct sensor_w_data color_no_adj[] = { + {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00, + /* default blue, green, red gain settings */ + 0x07, 0x00, 0x01}, 8} + }; + + const struct sensor_w_data vga_sensor1_init_data[] = { {0x11, 0x04, {0x01}, 1}, - /*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */ - {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, + {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, + /* These settings may be better for some cameras */ + /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */ 0x00, 0x0a}, 7}, {0x11, 0x04, {0x01}, 1}, {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6}, {0x11, 0x04, {0x01}, 1}, {0, 0, {0}, 0} }; + + if (sd->adj_colors) + err_code = sensor_write_regs(gspca_dev, color_adj, + ARRAY_SIZE(color_adj)); + else + err_code = sensor_write_regs(gspca_dev, color_no_adj, + ARRAY_SIZE(color_no_adj)); + + if (err_code < 0) + return err_code; + err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data, ARRAY_SIZE(vga_sensor1_init_data)); } - if (err_code < 0) - return err_code; - - msleep(200); - data[0] = 0x00; - data[1] = 0x4d; /* ISOC transfering enable... */ - err_code = mr_write(gspca_dev, 2); - return err_code; } @@ -719,97 +810,120 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int err_code; - struct cam *cam; - cam = &gspca_dev->cam; sd->sof_read = 0; - /* - * Some of the supported cameras require the memory pointer to be - * set to 0, or else they will not stream. - */ - zero_the_pointer(gspca_dev); - msleep(200); + + /* Some of the VGA cameras require the memory pointer + * to be set to 0 again. We have been forced to start the + * stream in sd_config() to detect the hardware, and closed it. + * Thus, we need here to do a completely fresh and clean start. */ + err_code = zero_the_pointer(gspca_dev); + if (err_code < 0) + return err_code; + + err_code = stream_start(gspca_dev); + if (err_code < 0) + return err_code; + if (sd->cam_type == CAM_TYPE_CIF) { err_code = start_cif_cam(gspca_dev); } else { err_code = start_vga_cam(gspca_dev); } - return err_code; + if (err_code < 0) + return err_code; + + setbrightness(gspca_dev); + setexposure(gspca_dev); + setgain(gspca_dev); + + return isoc_enable(gspca_dev); } static void sd_stopN(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int result; - - gspca_dev->usb_buf[0] = 1; - gspca_dev->usb_buf[1] = 0; - result = mr_write(gspca_dev, 2); - if (result < 0) - PDEBUG(D_ERR, "Camera Stop failed"); + stream_stop(gspca_dev); /* Not all the cams need this, but even if not, probably a good idea */ zero_the_pointer(gspca_dev); - if (sd->do_lcd_stop) { - gspca_dev->usb_buf[0] = 0x19; - gspca_dev->usb_buf[1] = 0x54; - result = mr_write(gspca_dev, 2); - if (result < 0) - PDEBUG(D_ERR, "Camera Stop failed"); - } + if (sd->do_lcd_stop) + lcd_stop(gspca_dev); } static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; u8 val; - - if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX)) + u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */ + u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */ + const u8 quick_clix_table[] = + /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */ + { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15}; + /* + * This control is disabled for CIF type 1 and VGA type 0 cameras. + * It does not quite act linearly for the Argus QuickClix camera, + * but it does control brightness. The values are 0 - 15 only, and + * the table above makes them act consecutively. + */ + if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) && + (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX))) return; - /* Note register 7 is also seen as 0x8x or 0xCx in dumps */ + if (sd->cam_type == CAM_TYPE_VGA) { + sign_reg += 4; + value_reg += 4; + } + + /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */ if (sd->brightness > 0) { - sensor_write1(gspca_dev, 7, 0x00); + sensor_write1(gspca_dev, sign_reg, 0x00); val = sd->brightness; } else { - sensor_write1(gspca_dev, 7, 0x01); - val = 257 - sd->brightness; + sensor_write1(gspca_dev, sign_reg, 0x01); + val = (257 - sd->brightness); } - sensor_write1(gspca_dev, 8, val); + /* Use lookup table for funky Argus QuickClix brightness */ + if (sd->do_lcd_stop) + val = quick_clix_table[val]; + + sensor_write1(gspca_dev, value_reg, val); } static void setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u8 val; + int exposure; + u8 buf[2]; if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX)) return; - if (sd->sensor_type) { - val = sd->exposure >> 4; - sensor_write1(gspca_dev, 3, val); - val = sd->exposure & 0xf; - sensor_write1(gspca_dev, 4, val); + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { + /* This cam does not like exposure settings < 300, + so scale 0 - 4095 to 300 - 4095 */ + exposure = (sd->exposure * 9267) / 10000 + 300; + sensor_write1(gspca_dev, 3, exposure >> 4); + sensor_write1(gspca_dev, 4, exposure & 0x0f); } else { - u8 clockdiv; - int exposure; - /* We have both a clock divider and an exposure register. We first calculate the clock divider, as that determines - the maximum exposure and then we calculayte the exposure + the maximum exposure and then we calculate the exposure register setting (which goes from 0 - 511). Note our 0 - 4095 exposure is mapped to 0 - 511 milliseconds exposure time */ - clockdiv = (60 * sd->exposure + 7999) / 8000; + u8 clockdiv = (60 * sd->exposure + 7999) / 8000; /* Limit framerate to not exceed usb bandwidth */ - if (clockdiv < 3 && gspca_dev->width >= 320) - clockdiv = 3; + if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320) + clockdiv = sd->min_clockdiv; else if (clockdiv < 2) clockdiv = 2; + if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4) + clockdiv = 4; + /* Frame exposure time in ms = 1000 * clockdiv / 60 -> exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */ exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv); @@ -819,9 +933,10 @@ static void setexposure(struct gspca_dev *gspca_dev) /* exposure register value is reversed! */ exposure = 511 - exposure; + buf[0] = exposure & 0xff; + buf[1] = exposure >> 8; + sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2); sensor_write1(gspca_dev, 0x02, clockdiv); - sensor_write1(gspca_dev, 0x0e, exposure & 0xff); - sensor_write1(gspca_dev, 0x0f, exposure >> 8); } } @@ -832,7 +947,7 @@ static void setgain(struct gspca_dev *gspca_dev) if (gspca_dev->ctrl_dis & (1 << GAIN_IDX)) return; - if (sd->sensor_type) { + if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) { sensor_write1(gspca_dev, 0x0e, sd->gain); } else { sensor_write1(gspca_dev, 0x10, sd->gain); @@ -893,17 +1008,35 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) return 0; } +static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->min_clockdiv = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->min_clockdiv; + return 0; +} + /* Include pac common sof detection functions */ #include "pac_common.h" static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ - int len) /* iso packet length */ + u8 *data, /* isoc packet */ + int len) /* iso packet length */ { + struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n; @@ -913,15 +1046,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n -= sizeof pac_sof_marker; else n = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, data, n); /* Start next frame. */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, pac_sof_marker, sizeof pac_sof_marker); len -= sof - data; data = sof; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } /* sub-driver description */ @@ -938,6 +1071,7 @@ static const struct sd_desc sd_desc = { /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */ {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */ {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */ {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */ diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index a5c190e93799d..ad9ec339981d5 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2,14 +2,19 @@ * OV519 driver * * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr) + * Copyright (C) 2009 Hans de Goede * * This module is adapted from the ov51x-jpeg package, which itself * was adapted from the ov511 driver. * * Original copyright for the ov511 driver is: * - * Copyright (c) 1999-2004 Mark W. McClelland + * Copyright (c) 1999-2006 Mark W. McClelland * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach + * Many improvements by Bret Wallach + * Color fixes by by Orion Sky Lawlor (2/26/2000) + * OV7620 fixes by Charl P. Botha + * Changes by Claudio Matsuoka * * ov51x-jpeg original copyright is: * @@ -58,6 +63,8 @@ struct sd { #define BRIDGE_OV518 2 #define BRIDGE_OV518PLUS 3 #define BRIDGE_OV519 4 +#define BRIDGE_OVFX2 5 +#define BRIDGE_W9968CF 6 #define BRIDGE_MASK 7 char invert_led; @@ -73,6 +80,10 @@ struct sd { __u8 vflip; __u8 autobrightness; __u8 freq; + __u8 quality; +#define QUALITY_MIN 50 +#define QUALITY_MAX 70 +#define QUALITY_DEF 50 __u8 stopped; /* Streaming is temporarily paused */ @@ -81,17 +92,31 @@ struct sd { char sensor; /* Type of image sensor chip (SEN_*) */ #define SEN_UNKNOWN 0 -#define SEN_OV6620 1 -#define SEN_OV6630 2 -#define SEN_OV66308AF 3 -#define SEN_OV7610 4 -#define SEN_OV7620 5 -#define SEN_OV7640 6 -#define SEN_OV7670 7 -#define SEN_OV76BE 8 -#define SEN_OV8610 9 +#define SEN_OV2610 1 +#define SEN_OV3610 2 +#define SEN_OV6620 3 +#define SEN_OV6630 4 +#define SEN_OV66308AF 5 +#define SEN_OV7610 6 +#define SEN_OV7620 7 +#define SEN_OV7640 8 +#define SEN_OV7670 9 +#define SEN_OV76BE 10 +#define SEN_OV8610 11 + + u8 sensor_addr; + int sensor_width; + int sensor_height; + int sensor_reg_cache[256]; + + u8 *jpeg_hdr; }; +/* Note this is a bit of a hack, but the w9968cf driver needs the code for all + the ov sensors which is already present here. When we have the time we + really should move the sensor drivers to v4l2 sub drivers. */ +#include "w996Xcf.c" + /* V4L2 controls supported by the driver */ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); @@ -345,6 +370,75 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { .priv = 0}, }; +static const struct v4l2_pix_format ovfx2_vga_mode[] = { + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_cif_mode[] = { + {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 160, + .sizeimage = 160 * 120, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 3}, + {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 176, + .sizeimage = 176 * 144, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 320, + .sizeimage = 320 * 240, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 2}, + {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 352, + .sizeimage = 352 * 288, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; +static const struct v4l2_pix_format ovfx2_ov2610_mode[] = { + {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB}, +}; +static const struct v4l2_pix_format ovfx2_ov3610_mode[] = { + {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, + {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 1600, + .sizeimage = 1600 * 1200, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, + {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE, + .bytesperline = 2048, + .sizeimage = 2048 * 1536, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 0}, +}; + + /* Registers common to OV511 / OV518 */ #define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */ #define R51x_SYS_RESET 0x50 @@ -406,6 +500,30 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { #define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ +/* + * The FX2 chip does not give us a zero length read at end of frame. + * It does, however, give a short read at the end of a frame, if + * neccessary, rather than run two frames together. + * + * By choosing the right bulk transfer size, we are guaranteed to always + * get a short read for the last read of each frame. Frame sizes are + * always a composite number (width * height, or a multiple) so if we + * choose a prime number, we are guaranteed that the last read of a + * frame will be short. + * + * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB, + * otherwise EOVERFLOW "babbling" errors occur. I have not been able + * to figure out why. [PMiller] + * + * The constant (13 * 4096) is the largest "prime enough" number less than 64KB. + * + * It isn't enough to know the number of bytes per frame, in case we + * have data dropouts or buffer overruns (even though the FX2 double + * buffers, there are some pretty strict real time constraints for + * isochronous transfer for larger frame sizes). + */ +#define OVFX2_BULK_SIZE (13 * 4096) + /* I2C registers */ #define R51x_I2C_W_SID 0x41 #define R51x_I2C_SADDR_3 0x42 @@ -413,9 +531,11 @@ static const struct v4l2_pix_format ov511_sif_mode[] = { #define R51x_I2C_R_SID 0x44 #define R51x_I2C_DATA 0x45 #define R518_I2C_CTL 0x47 /* OV518(+) only */ +#define OVFX2_I2C_ADDR 0x00 /* I2C ADDRESSES */ #define OV7xx0_SID 0x42 +#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */ #define OV8xx0_SID 0xa0 #define OV6xx0_SID 0xc0 @@ -508,6 +628,696 @@ struct ov_i2c_regvals { __u8 val; }; +/* Settings for OV2610 camera chip */ +static const struct ov_i2c_regvals norm_2610[] = +{ + { 0x12, 0x80 }, /* reset */ +}; + +static const struct ov_i2c_regvals norm_3620b[] = +{ + /* + * From the datasheet: "Note that after writing to register COMH + * (0x12) to change the sensor mode, registers related to the + * sensor’s cropping window will be reset back to their default + * values." + * + * "wait 4096 external clock ... to make sure the sensor is + * stable and ready to access registers" i.e. 160us at 24MHz + */ + + { 0x12, 0x80 }, /* COMH reset */ + { 0x12, 0x00 }, /* QXGA, master */ + + /* + * 11 CLKRC "Clock Rate Control" + * [7] internal frequency doublers: on + * [6] video port mode: master + * [5:0] clock divider: 1 + */ + { 0x11, 0x80 }, + + /* + * 13 COMI "Common Control I" + * = 192 (0xC0) 11000000 + * COMI[7] "AEC speed selection" + * = 1 (0x01) 1....... "Faster AEC correction" + * COMI[6] "AEC speed step selection" + * = 1 (0x01) .1...... "Big steps, fast" + * COMI[5] "Banding filter on off" + * = 0 (0x00) ..0..... "Off" + * COMI[4] "Banding filter option" + * = 0 (0x00) ...0.... "Main clock is 48 MHz and + * the PLL is ON" + * COMI[3] "Reserved" + * = 0 (0x00) ....0... + * COMI[2] "AGC auto manual control selection" + * = 0 (0x00) .....0.. "Manual" + * COMI[1] "AWB auto manual control selection" + * = 0 (0x00) ......0. "Manual" + * COMI[0] "Exposure control" + * = 0 (0x00) .......0 "Manual" + */ + { 0x13, 0xC0 }, + + /* + * 09 COMC "Common Control C" + * = 8 (0x08) 00001000 + * COMC[7:5] "Reserved" + * = 0 (0x00) 000..... + * COMC[4] "Sleep Mode Enable" + * = 0 (0x00) ...0.... "Normal mode" + * COMC[3:2] "Sensor sampling reset timing selection" + * = 2 (0x02) ....10.. "Longer reset time" + * COMC[1:0] "Output drive current select" + * = 0 (0x00) ......00 "Weakest" + */ + { 0x09, 0x08 }, + + /* + * 0C COMD "Common Control D" + * = 8 (0x08) 00001000 + * COMD[7] "Reserved" + * = 0 (0x00) 0....... + * COMD[6] "Swap MSB and LSB at the output port" + * = 0 (0x00) .0...... "False" + * COMD[5:3] "Reserved" + * = 1 (0x01) ..001... + * COMD[2] "Output Average On Off" + * = 0 (0x00) .....0.. "Output Normal" + * COMD[1] "Sensor precharge voltage selection" + * = 0 (0x00) ......0. "Selects internal + * reference precharge + * voltage" + * COMD[0] "Snapshot option" + * = 0 (0x00) .......0 "Enable live video output + * after snapshot sequence" + */ + { 0x0c, 0x08 }, + + /* + * 0D COME "Common Control E" + * = 161 (0xA1) 10100001 + * COME[7] "Output average option" + * = 1 (0x01) 1....... "Output average of 4 pixels" + * COME[6] "Anti-blooming control" + * = 0 (0x00) .0...... "Off" + * COME[5:3] "Reserved" + * = 4 (0x04) ..100... + * COME[2] "Clock output power down pin status" + * = 0 (0x00) .....0.. "Tri-state data output pin + * on power down" + * COME[1] "Data output pin status selection at power down" + * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK, + * HREF, and CHSYNC pins on + * power down" + * COME[0] "Auto zero circuit select" + * = 1 (0x01) .......1 "On" + */ + { 0x0d, 0xA1 }, + + /* + * 0E COMF "Common Control F" + * = 112 (0x70) 01110000 + * COMF[7] "System clock selection" + * = 0 (0x00) 0....... "Use 24 MHz system clock" + * COMF[6:4] "Reserved" + * = 7 (0x07) .111.... + * COMF[3] "Manual auto negative offset canceling selection" + * = 0 (0x00) ....0... "Auto detect negative + * offset and cancel it" + * COMF[2:0] "Reserved" + * = 0 (0x00) .....000 + */ + { 0x0e, 0x70 }, + + /* + * 0F COMG "Common Control G" + * = 66 (0x42) 01000010 + * COMG[7] "Optical black output selection" + * = 0 (0x00) 0....... "Disable" + * COMG[6] "Black level calibrate selection" + * = 1 (0x01) .1...... "Use optical black pixels + * to calibrate" + * COMG[5:4] "Reserved" + * = 0 (0x00) ..00.... + * COMG[3] "Channel offset adjustment" + * = 0 (0x00) ....0... "Disable offset adjustment" + * COMG[2] "ADC black level calibration option" + * = 0 (0x00) .....0.. "Use B/G line and G/R + * line to calibrate each + * channel's black level" + * COMG[1] "Reserved" + * = 1 (0x01) ......1. + * COMG[0] "ADC black level calibration enable" + * = 0 (0x00) .......0 "Disable" + */ + { 0x0f, 0x42 }, + + /* + * 14 COMJ "Common Control J" + * = 198 (0xC6) 11000110 + * COMJ[7:6] "AGC gain ceiling" + * = 3 (0x03) 11...... "8x" + * COMJ[5:4] "Reserved" + * = 0 (0x00) ..00.... + * COMJ[3] "Auto banding filter" + * = 0 (0x00) ....0... "Banding filter is always + * on off depending on + * COMI[5] setting" + * COMJ[2] "VSYNC drop option" + * = 1 (0x01) .....1.. "SYNC is dropped if frame + * data is dropped" + * COMJ[1] "Frame data drop" + * = 1 (0x01) ......1. "Drop frame data if + * exposure is not within + * tolerance. In AEC mode, + * data is normally dropped + * when data is out of + * range." + * COMJ[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x14, 0xC6 }, + + /* + * 15 COMK "Common Control K" + * = 2 (0x02) 00000010 + * COMK[7] "CHSYNC pin output swap" + * = 0 (0x00) 0....... "CHSYNC" + * COMK[6] "HREF pin output swap" + * = 0 (0x00) .0...... "HREF" + * COMK[5] "PCLK output selection" + * = 0 (0x00) ..0..... "PCLK always output" + * COMK[4] "PCLK edge selection" + * = 0 (0x00) ...0.... "Data valid on falling edge" + * COMK[3] "HREF output polarity" + * = 0 (0x00) ....0... "positive" + * COMK[2] "Reserved" + * = 0 (0x00) .....0.. + * COMK[1] "VSYNC polarity" + * = 1 (0x01) ......1. "negative" + * COMK[0] "HSYNC polarity" + * = 0 (0x00) .......0 "positive" + */ + { 0x15, 0x02 }, + + /* + * 33 CHLF "Current Control" + * = 9 (0x09) 00001001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 0 (0x00) ...0.... "normal current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x09 }, + + /* + * 34 VBLM "Blooming Control" + * = 80 (0x50) 01010000 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 0 (0x00) ....0000 + */ + { 0x34, 0x50 }, + + /* + * 36 VCHG "Sensor Precharge Voltage Control" + * = 0 (0x00) 00000000 + * VCHG[7] "Reserved" + * = 0 (0x00) 0....... + * VCHG[6:4] "Sensor precharge voltage control" + * = 0 (0x00) .000.... + * VCHG[3:0] "Sensor array common reference" + * = 0 (0x00) ....0000 + */ + { 0x36, 0x00 }, + + /* + * 37 ADC "ADC Reference Control" + * = 4 (0x04) 00000100 + * ADC[7:4] "Reserved" + * = 0 (0x00) 0000.... + * ADC[3] "ADC input signal range" + * = 0 (0x00) ....0... "Input signal 1.0x" + * ADC[2:0] "ADC range control" + * = 4 (0x04) .....100 + */ + { 0x37, 0x04 }, + + /* + * 38 ACOM "Analog Common Ground" + * = 82 (0x52) 01010010 + * ACOM[7] "Analog gain control" + * = 0 (0x00) 0....... "Gain 1x" + * ACOM[6] "Analog black level calibration" + * = 1 (0x01) .1...... "On" + * ACOM[5:0] "Reserved" + * = 18 (0x12) ..010010 + */ + { 0x38, 0x52 }, + + /* + * 3A FREFA "Internal Reference Adjustment" + * = 0 (0x00) 00000000 + * FREFA[7:0] "Range" + * = 0 (0x00) 00000000 + */ + { 0x3a, 0x00 }, + + /* + * 3C FVOPT "Internal Reference Adjustment" + * = 31 (0x1F) 00011111 + * FVOPT[7:0] "Range" + * = 31 (0x1F) 00011111 + */ + { 0x3c, 0x1F }, + + /* + * 44 Undocumented = 0 (0x00) 00000000 + * 44[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x44, 0x00 }, + + /* + * 40 Undocumented = 0 (0x00) 00000000 + * 40[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x40, 0x00 }, + + /* + * 41 Undocumented = 0 (0x00) 00000000 + * 41[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x41, 0x00 }, + + /* + * 42 Undocumented = 0 (0x00) 00000000 + * 42[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x42, 0x00 }, + + /* + * 43 Undocumented = 0 (0x00) 00000000 + * 43[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x43, 0x00 }, + + /* + * 45 Undocumented = 128 (0x80) 10000000 + * 45[7:0] "It's a secret" + * = 128 (0x80) 10000000 + */ + { 0x45, 0x80 }, + + /* + * 48 Undocumented = 192 (0xC0) 11000000 + * 48[7:0] "It's a secret" + * = 192 (0xC0) 11000000 + */ + { 0x48, 0xC0 }, + + /* + * 49 Undocumented = 25 (0x19) 00011001 + * 49[7:0] "It's a secret" + * = 25 (0x19) 00011001 + */ + { 0x49, 0x19 }, + + /* + * 4B Undocumented = 128 (0x80) 10000000 + * 4B[7:0] "It's a secret" + * = 128 (0x80) 10000000 + */ + { 0x4B, 0x80 }, + + /* + * 4D Undocumented = 196 (0xC4) 11000100 + * 4D[7:0] "It's a secret" + * = 196 (0xC4) 11000100 + */ + { 0x4D, 0xC4 }, + + /* + * 35 VREF "Reference Voltage Control" + * = 76 (0x4C) 01001100 + * VREF[7:5] "Column high reference control" + * = 2 (0x02) 010..... "higher voltage" + * VREF[4:2] "Column low reference control" + * = 3 (0x03) ...011.. "Highest voltage" + * VREF[1:0] "Reserved" + * = 0 (0x00) ......00 + */ + { 0x35, 0x4C }, + + /* + * 3D Undocumented = 0 (0x00) 00000000 + * 3D[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x3D, 0x00 }, + + /* + * 3E Undocumented = 0 (0x00) 00000000 + * 3E[7:0] "It's a secret" + * = 0 (0x00) 00000000 + */ + { 0x3E, 0x00 }, + + /* + * 3B FREFB "Internal Reference Adjustment" + * = 24 (0x18) 00011000 + * FREFB[7:0] "Range" + * = 24 (0x18) 00011000 + */ + { 0x3b, 0x18 }, + + /* + * 33 CHLF "Current Control" + * = 25 (0x19) 00011001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 1 (0x01) ...1.... "double current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x19 }, + + /* + * 34 VBLM "Blooming Control" + * = 90 (0x5A) 01011010 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 10 (0x0A) ....1010 + */ + { 0x34, 0x5A }, + + /* + * 3B FREFB "Internal Reference Adjustment" + * = 0 (0x00) 00000000 + * FREFB[7:0] "Range" + * = 0 (0x00) 00000000 + */ + { 0x3b, 0x00 }, + + /* + * 33 CHLF "Current Control" + * = 9 (0x09) 00001001 + * CHLF[7:6] "Sensor current control" + * = 0 (0x00) 00...... + * CHLF[5] "Sensor current range control" + * = 0 (0x00) ..0..... "normal range" + * CHLF[4] "Sensor current" + * = 0 (0x00) ...0.... "normal current" + * CHLF[3] "Sensor buffer current control" + * = 1 (0x01) ....1... "half current" + * CHLF[2] "Column buffer current control" + * = 0 (0x00) .....0.. "normal current" + * CHLF[1] "Analog DSP current control" + * = 0 (0x00) ......0. "normal current" + * CHLF[1] "ADC current control" + * = 0 (0x00) ......0. "normal current" + */ + { 0x33, 0x09 }, + + /* + * 34 VBLM "Blooming Control" + * = 80 (0x50) 01010000 + * VBLM[7] "Hard soft reset switch" + * = 0 (0x00) 0....... "Hard reset" + * VBLM[6:4] "Blooming voltage selection" + * = 5 (0x05) .101.... + * VBLM[3:0] "Sensor current control" + * = 0 (0x00) ....0000 + */ + { 0x34, 0x50 }, + + /* + * 12 COMH "Common Control H" + * = 64 (0x40) 01000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 4 (0x04) .100.... "XGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x40 }, + + /* + * 17 HREFST "Horizontal window start" + * = 31 (0x1F) 00011111 + * HREFST[7:0] "Horizontal window start, 8 MSBs" + * = 31 (0x1F) 00011111 + */ + { 0x17, 0x1F }, + + /* + * 18 HREFEND "Horizontal window end" + * = 95 (0x5F) 01011111 + * HREFEND[7:0] "Horizontal Window End, 8 MSBs" + * = 95 (0x5F) 01011111 + */ + { 0x18, 0x5F }, + + /* + * 19 VSTRT "Vertical window start" + * = 0 (0x00) 00000000 + * VSTRT[7:0] "Vertical Window Start, 8 MSBs" + * = 0 (0x00) 00000000 + */ + { 0x19, 0x00 }, + + /* + * 1A VEND "Vertical window end" + * = 96 (0x60) 01100000 + * VEND[7:0] "Vertical Window End, 8 MSBs" + * = 96 (0x60) 01100000 + */ + { 0x1a, 0x60 }, + + /* + * 32 COMM "Common Control M" + * = 18 (0x12) 00010010 + * COMM[7:6] "Pixel clock divide option" + * = 0 (0x00) 00...... "/1" + * COMM[5:3] "Horizontal window end position, 3 LSBs" + * = 2 (0x02) ..010... + * COMM[2:0] "Horizontal window start position, 3 LSBs" + * = 2 (0x02) .....010 + */ + { 0x32, 0x12 }, + + /* + * 03 COMA "Common Control A" + * = 74 (0x4A) 01001010 + * COMA[7:4] "AWB Update Threshold" + * = 4 (0x04) 0100.... + * COMA[3:2] "Vertical window end line control 2 LSBs" + * = 2 (0x02) ....10.. + * COMA[1:0] "Vertical window start line control 2 LSBs" + * = 2 (0x02) ......10 + */ + { 0x03, 0x4A }, + + /* + * 11 CLKRC "Clock Rate Control" + * = 128 (0x80) 10000000 + * CLKRC[7] "Internal frequency doublers on off seclection" + * = 1 (0x01) 1....... "On" + * CLKRC[6] "Digital video master slave selection" + * = 0 (0x00) .0...... "Master mode, sensor + * provides PCLK" + * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }" + * = 0 (0x00) ..000000 + */ + { 0x11, 0x80 }, + + /* + * 12 COMH "Common Control H" + * = 0 (0x00) 00000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 0 (0x00) .000.... "QXGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x00 }, + + /* + * 12 COMH "Common Control H" + * = 64 (0x40) 01000000 + * COMH[7] "SRST" + * = 0 (0x00) 0....... "No-op" + * COMH[6:4] "Resolution selection" + * = 4 (0x04) .100.... "XGA" + * COMH[3] "Master slave selection" + * = 0 (0x00) ....0... "Master mode" + * COMH[2] "Internal B/R channel option" + * = 0 (0x00) .....0.. "B/R use same channel" + * COMH[1] "Color bar test pattern" + * = 0 (0x00) ......0. "Off" + * COMH[0] "Reserved" + * = 0 (0x00) .......0 + */ + { 0x12, 0x40 }, + + /* + * 17 HREFST "Horizontal window start" + * = 31 (0x1F) 00011111 + * HREFST[7:0] "Horizontal window start, 8 MSBs" + * = 31 (0x1F) 00011111 + */ + { 0x17, 0x1F }, + + /* + * 18 HREFEND "Horizontal window end" + * = 95 (0x5F) 01011111 + * HREFEND[7:0] "Horizontal Window End, 8 MSBs" + * = 95 (0x5F) 01011111 + */ + { 0x18, 0x5F }, + + /* + * 19 VSTRT "Vertical window start" + * = 0 (0x00) 00000000 + * VSTRT[7:0] "Vertical Window Start, 8 MSBs" + * = 0 (0x00) 00000000 + */ + { 0x19, 0x00 }, + + /* + * 1A VEND "Vertical window end" + * = 96 (0x60) 01100000 + * VEND[7:0] "Vertical Window End, 8 MSBs" + * = 96 (0x60) 01100000 + */ + { 0x1a, 0x60 }, + + /* + * 32 COMM "Common Control M" + * = 18 (0x12) 00010010 + * COMM[7:6] "Pixel clock divide option" + * = 0 (0x00) 00...... "/1" + * COMM[5:3] "Horizontal window end position, 3 LSBs" + * = 2 (0x02) ..010... + * COMM[2:0] "Horizontal window start position, 3 LSBs" + * = 2 (0x02) .....010 + */ + { 0x32, 0x12 }, + + /* + * 03 COMA "Common Control A" + * = 74 (0x4A) 01001010 + * COMA[7:4] "AWB Update Threshold" + * = 4 (0x04) 0100.... + * COMA[3:2] "Vertical window end line control 2 LSBs" + * = 2 (0x02) ....10.. + * COMA[1:0] "Vertical window start line control 2 LSBs" + * = 2 (0x02) ......10 + */ + { 0x03, 0x4A }, + + /* + * 02 RED "Red Gain Control" + * = 175 (0xAF) 10101111 + * RED[7] "Action" + * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" + * RED[6:0] "Value" + * = 47 (0x2F) .0101111 + */ + { 0x02, 0xAF }, + + /* + * 2D ADDVSL "VSYNC Pulse Width" + * = 210 (0xD2) 11010010 + * ADDVSL[7:0] "VSYNC pulse width, LSB" + * = 210 (0xD2) 11010010 + */ + { 0x2d, 0xD2 }, + + /* + * 00 GAIN = 24 (0x18) 00011000 + * GAIN[7:6] "Reserved" + * = 0 (0x00) 00...... + * GAIN[5] "Double" + * = 0 (0x00) ..0..... "False" + * GAIN[4] "Double" + * = 1 (0x01) ...1.... "True" + * GAIN[3:0] "Range" + * = 8 (0x08) ....1000 + */ + { 0x00, 0x18 }, + + /* + * 01 BLUE "Blue Gain Control" + * = 240 (0xF0) 11110000 + * BLUE[7] "Action" + * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))" + * BLUE[6:0] "Value" + * = 112 (0x70) .1110000 + */ + { 0x01, 0xF0 }, + + /* + * 10 AEC "Automatic Exposure Control" + * = 10 (0x0A) 00001010 + * AEC[7:0] "Automatic Exposure Control, 8 MSBs" + * = 10 (0x0A) 00001010 + */ + { 0x10, 0x0A }, + + { 0xE1, 0x67 }, + { 0xE3, 0x03 }, + { 0xE4, 0x26 }, + { 0xE5, 0x3E }, + { 0xF8, 0x01 }, + { 0xFF, 0x01 }, +}; + static const struct ov_i2c_regvals norm_6x20[] = { { 0x12, 0x80 }, /* reset */ { 0x11, 0x01 }, @@ -678,6 +1488,7 @@ static const struct ov_i2c_regvals norm_7610[] = { }; static const struct ov_i2c_regvals norm_7620[] = { + { 0x12, 0x80 }, /* reset */ { 0x00, 0x00 }, /* gain */ { 0x01, 0x80 }, /* blue gain */ { 0x02, 0x80 }, /* red gain */ @@ -1042,10 +1853,28 @@ static unsigned char ov7670_abs_to_sm(unsigned char v) } /* Write a OV519 register */ -static int reg_w(struct sd *sd, __u16 index, __u8 value) +static int reg_w(struct sd *sd, __u16 index, __u16 value) { - int ret; - int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 2 : 1; + int ret, req = 0; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 2; + break; + case BRIDGE_OVFX2: + req = 0x0a; + /* fall through */ + case BRIDGE_W9968CF: + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, NULL, 0, 500); + goto leave; + default: + req = 1; + } sd->gspca_dev.usb_buf[0] = value; ret = usb_control_msg(sd->gspca_dev.dev, @@ -1054,17 +1883,35 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, 1, 500); - if (ret < 0) - PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value); - return ret; +leave: + if (ret < 0) { + PDEBUG(D_ERR, "Write reg 0x%04x -> [0x%02x] failed", + value, index); + return ret; + } + + PDEBUG(D_USBO, "Write reg 0x%04x -> [0x%02x]", value, index); + return 0; } -/* Read from a OV519 register */ +/* Read from a OV519 register, note not valid for the w9968cf!! */ /* returns: negative is error, pos or zero is data */ static int reg_r(struct sd *sd, __u16 index) { int ret; - int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 3 : 1; + int req; + + switch (sd->bridge) { + case BRIDGE_OV511: + case BRIDGE_OV511PLUS: + req = 3; + break; + case BRIDGE_OVFX2: + req = 0x0b; + break; + default: + req = 1; + } ret = usb_control_msg(sd->gspca_dev.dev, usb_rcvctrlpipe(sd->gspca_dev.dev, 0), @@ -1072,10 +1919,12 @@ static int reg_r(struct sd *sd, __u16 index) USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, 1, 500); - if (ret >= 0) + if (ret >= 0) { ret = sd->gspca_dev.usb_buf[0]; - else + PDEBUG(D_USBI, "Read reg [0x%02X] -> 0x%04X", index, ret); + } else PDEBUG(D_ERR, "Read reg [0x%02x] failed", index); + return ret; } @@ -1095,6 +1944,7 @@ static int reg_r8(struct sd *sd, ret = sd->gspca_dev.usb_buf[0]; else PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index); + return ret; } @@ -1140,9 +1990,12 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n) USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, sd->gspca_dev.usb_buf, n, 500); - if (ret < 0) + if (ret < 0) { PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value); - return ret; + return ret; + } + + return 0; } static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value) @@ -1324,32 +2177,110 @@ static int ov518_i2c_r(struct sd *sd, __u8 reg) return value; } +static int ovfx2_i2c_w(struct sd *sd, __u8 reg, __u8 value) +{ + int ret; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 0x02, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + (__u16)value, (__u16)reg, NULL, 0, 500); + + if (ret < 0) { + PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg); + return ret; + } + + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + return 0; +} + +static int ovfx2_i2c_r(struct sd *sd, __u8 reg) +{ + int ret; + + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 0x03, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, (__u16)reg, sd->gspca_dev.usb_buf, 1, 500); + + if (ret >= 0) { + ret = sd->gspca_dev.usb_buf[0]; + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, ret); + } else + PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); + + return ret; +} + static int i2c_w(struct sd *sd, __u8 reg, __u8 value) { + int ret = -1; + + if (sd->sensor_reg_cache[reg] == value) + return 0; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - return ov511_i2c_w(sd, reg, value); + ret = ov511_i2c_w(sd, reg, value); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: case BRIDGE_OV519: - return ov518_i2c_w(sd, reg, value); + ret = ov518_i2c_w(sd, reg, value); + break; + case BRIDGE_OVFX2: + ret = ovfx2_i2c_w(sd, reg, value); + break; + case BRIDGE_W9968CF: + ret = w9968cf_i2c_w(sd, reg, value); + break; } - return -1; /* Should never happen */ + + if (ret >= 0) { + /* Up on sensor reset empty the register cache */ + if (reg == 0x12 && (value & 0x80)) + memset(sd->sensor_reg_cache, -1, + sizeof(sd->sensor_reg_cache)); + else + sd->sensor_reg_cache[reg] = value; + } + + return ret; } static int i2c_r(struct sd *sd, __u8 reg) { + int ret = -1; + + if (sd->sensor_reg_cache[reg] != -1) + return sd->sensor_reg_cache[reg]; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - return ov511_i2c_r(sd, reg); + ret = ov511_i2c_r(sd, reg); + break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: case BRIDGE_OV519: - return ov518_i2c_r(sd, reg); + ret = ov518_i2c_r(sd, reg); + break; + case BRIDGE_OVFX2: + ret = ovfx2_i2c_r(sd, reg); + break; + case BRIDGE_W9968CF: + ret = w9968cf_i2c_r(sd, reg); + break; } - return -1; /* Should never happen */ + + if (ret >= 0) + sd->sensor_reg_cache[reg] = ret; + + return ret; } /* Writes bits at positions specified by mask to an I2C reg. Bits that are in @@ -1389,6 +2320,10 @@ static inline int ov51x_stop(struct sd *sd) return reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a); case BRIDGE_OV519: return reg_w(sd, OV519_SYS_RESET1, 0x0f); + case BRIDGE_OVFX2: + return reg_w_mask(sd, 0x0f, 0x00, 0x02); + case BRIDGE_W9968CF: + return reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */ } return 0; @@ -1418,18 +2353,27 @@ static inline int ov51x_restart(struct sd *sd) return reg_w(sd, R51x_SYS_RESET, 0x00); case BRIDGE_OV519: return reg_w(sd, OV519_SYS_RESET1, 0x00); + case BRIDGE_OVFX2: + return reg_w_mask(sd, 0x0f, 0x02, 0x02); + case BRIDGE_W9968CF: + return reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */ } return 0; } +static int ov51x_set_slave_ids(struct sd *sd, __u8 slave); + /* This does an initial reset of an OmniVision sensor and ensures that I2C * is synchronized. Returns <0 on failure. */ -static int init_ov_sensor(struct sd *sd) +static int init_ov_sensor(struct sd *sd, __u8 slave) { int i; + if (ov51x_set_slave_ids(sd, slave) < 0) + return -EIO; + /* Reset the sensor */ if (i2c_w(sd, 0x12, 0x80) < 0) return -EIO; @@ -1466,6 +2410,14 @@ static int ov51x_set_slave_ids(struct sd *sd, { int rc; + switch (sd->bridge) { + case BRIDGE_OVFX2: + return reg_w(sd, OVFX2_I2C_ADDR, slave); + case BRIDGE_W9968CF: + sd->sensor_addr = slave; + return 0; + } + rc = reg_w(sd, R51x_I2C_W_SID, slave); if (rc < 0) return rc; @@ -1508,6 +2460,39 @@ static int write_i2c_regvals(struct sd *sd, * ***************************************************************************/ +/* This initializes the OV2x10 / OV3610 / OV3620 */ +static int ov_hires_configure(struct sd *sd) +{ + int high, low; + + if (sd->bridge != BRIDGE_OVFX2) { + PDEBUG(D_ERR, "error hires sensors only supported with ovfx2"); + return -1; + } + + PDEBUG(D_PROBE, "starting ov hires configuration"); + + /* Detect sensor (sub)type */ + high = i2c_r(sd, 0x0a); + low = i2c_r(sd, 0x0b); + /* info("%x, %x", high, low); */ + if (high == 0x96 && low == 0x40) { + PDEBUG(D_PROBE, "Sensor is an OV2610"); + sd->sensor = SEN_OV2610; + } else if (high == 0x36 && (low & 0x0f) == 0x00) { + PDEBUG(D_PROBE, "Sensor is an OV3610"); + sd->sensor = SEN_OV3610; + } else { + PDEBUG(D_ERR, "Error unknown sensor type: 0x%02x%02x", + high, low); + return -1; + } + + /* Set sensor-specific vars */ + return 0; +} + + /* This initializes the OV8110, OV8610 sensor. The OV8110 uses * the same register settings as the OV8610, since they are very similar. */ @@ -1966,12 +2951,29 @@ static int ov519_configure(struct sd *sd) return write_regvals(sd, init_519, ARRAY_SIZE(init_519)); } +static int ovfx2_configure(struct sd *sd) +{ + static const struct ov_regvals init_fx2[] = { + { 0x00, 0x60 }, + { 0x02, 0x01 }, + { 0x0f, 0x1d }, + { 0xe9, 0x82 }, + { 0xea, 0xc7 }, + { 0xeb, 0x10 }, + { 0xec, 0xf6 }, + }; + + sd->stopped = 1; + + return write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2)); +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; + struct cam *cam = &gspca_dev->cam; int ret = 0; sd->bridge = id->driver_info & BRIDGE_MASK; @@ -1989,6 +2991,16 @@ static int sd_config(struct gspca_dev *gspca_dev, case BRIDGE_OV519: ret = ov519_configure(sd); break; + case BRIDGE_OVFX2: + ret = ovfx2_configure(sd); + cam->bulk_size = OVFX2_BULK_SIZE; + cam->bulk_nurbs = MAX_NURBS; + cam->bulk = 1; + break; + case BRIDGE_W9968CF: + ret = w9968cf_configure(sd); + cam->reverse_alts = 1; + break; } if (ret) @@ -1996,49 +3008,39 @@ static int sd_config(struct gspca_dev *gspca_dev, ov51x_led_control(sd, 0); /* turn LED off */ - /* Test for 76xx */ - if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0) - goto error; - /* The OV519 must be more aggressive about sensor detection since * I2C write will never fail if the sensor is not present. We have * to try to initialize the sensor to detect its presence */ - if (init_ov_sensor(sd) >= 0) { + + /* Test for 76xx */ + if (init_ov_sensor(sd, OV7xx0_SID) >= 0) { if (ov7xx0_configure(sd) < 0) { PDEBUG(D_ERR, "Failed to configure OV7xx0"); goto error; } - } else { - - /* Test for 6xx0 */ - if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0) + /* Test for 6xx0 */ + } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) { + if (ov6xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV6xx0"); + goto error; + } + /* Test for 8xx0 */ + } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) { + if (ov8xx0_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure OV8xx0"); goto error; - - if (init_ov_sensor(sd) >= 0) { - if (ov6xx0_configure(sd) < 0) { - PDEBUG(D_ERR, "Failed to configure OV6xx0"); - goto error; - } - } else { - - /* Test for 8xx0 */ - if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0) - goto error; - - if (init_ov_sensor(sd) < 0) { - PDEBUG(D_ERR, - "Can't determine sensor slave IDs"); - goto error; - } - if (ov8xx0_configure(sd) < 0) { - PDEBUG(D_ERR, - "Failed to configure OV8xx0 sensor"); - goto error; - } } + /* Test for 3xxx / 2xxx */ + } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) { + if (ov_hires_configure(sd) < 0) { + PDEBUG(D_ERR, "Failed to configure high res OV"); + goto error; + } + } else { + PDEBUG(D_ERR, "Can't determine sensor slave IDs"); + goto error; } - cam = &gspca_dev->cam; switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: @@ -2069,6 +3071,31 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->nmodes = ARRAY_SIZE(ov519_sif_mode); } break; + case BRIDGE_OVFX2: + if (sd->sensor == SEN_OV2610) { + cam->cam_mode = ovfx2_ov2610_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode); + } else if (sd->sensor == SEN_OV3610) { + cam->cam_mode = ovfx2_ov3610_mode; + cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode); + } else if (!sd->sif) { + cam->cam_mode = ov519_vga_mode; + cam->nmodes = ARRAY_SIZE(ov519_vga_mode); + } else { + cam->cam_mode = ov519_sif_mode; + cam->nmodes = ARRAY_SIZE(ov519_sif_mode); + } + break; + case BRIDGE_W9968CF: + cam->cam_mode = w9968cf_vga_mode; + cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode); + if (sd->sif) + cam->nmodes--; + + /* w9968cf needs initialisation once the sensor is known */ + if (w9968cf_init(sd) < 0) + goto error; + break; } sd->brightness = BRIGHTNESS_DEF; if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF) @@ -2087,11 +3114,15 @@ static int sd_config(struct gspca_dev *gspca_dev, gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << OV7670_FREQ_IDX); } + sd->quality = QUALITY_DEF; if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX; /* OV8610 Frequency filter control should work but needs testing */ if (sd->sensor == SEN_OV8610) gspca_dev->ctrl_dis |= 1 << FREQ_IDX; + /* No controls for the OV2610/OV3610 */ + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) + gspca_dev->ctrl_dis |= 0xFF; return 0; error: @@ -2106,6 +3137,20 @@ static int sd_init(struct gspca_dev *gspca_dev) /* initialize the sensor */ switch (sd->sensor) { + case SEN_OV2610: + if (write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610))) + return -EIO; + /* Enable autogain, autoexpo, awb, bandfilter */ + if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0) + return -EIO; + break; + case SEN_OV3610: + if (write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b))) + return -EIO; + /* Enable autogain, autoexpo, awb, bandfilter */ + if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0) + return -EIO; + break; case SEN_OV6620: if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20))) return -EIO; @@ -2548,19 +3593,60 @@ static int ov519_mode_init_regs(struct sd *sd) static int mode_init_ov_sensor_regs(struct sd *sd) { struct gspca_dev *gspca_dev; - int qvga; + int qvga, xstart, xend, ystart, yend; + __u8 v; gspca_dev = &sd->gspca_dev; qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; /******** Mode (VGA/QVGA) and sensor specific regs ********/ switch (sd->sensor) { + case SEN_OV2610: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20); + i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); + i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); + i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); + i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); + i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + return 0; + case SEN_OV3610: + if (qvga) { + xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4); + ystart = (776 - gspca_dev->height) / 2; + } else { + xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4); + ystart = (1544 - gspca_dev->height) / 2; + } + xend = xstart + gspca_dev->width; + yend = ystart + gspca_dev->height; + /* Writing to the COMH register resets the other windowing regs + to their default values, so we must do this first. */ + i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0); + i2c_w_mask(sd, 0x32, + (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7), + 0x3f); + i2c_w_mask(sd, 0x03, + (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3), + 0x0f); + i2c_w(sd, 0x17, xstart >> 4); + i2c_w(sd, 0x18, xend >> 4); + i2c_w(sd, 0x19, ystart >> 3); + i2c_w(sd, 0x1a, yend >> 3); + return 0; case SEN_OV8610: /* For OV8610 qvga means qsvga */ i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */ + i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */ break; case SEN_OV7610: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w(sd, 0x35, qvga?0x1e:0x9e); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ break; case SEN_OV7620: case SEN_OV76BE: @@ -2571,6 +3657,10 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0); i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + if (sd->sensor == SEN_OV76BE) + i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e); break; case SEN_OV7640: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); @@ -2580,6 +3670,7 @@ static int mode_init_ov_sensor_regs(struct sd *sd) /* i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */ /* i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */ /* i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */ + i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */ break; case SEN_OV7670: /* set COM7_FMT_VGA or COM7_FMT_QVGA @@ -2588,55 +3679,56 @@ static int mode_init_ov_sensor_regs(struct sd *sd) i2c_w_mask(sd, OV7670_REG_COM7, qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA, OV7670_COM7_FMT_MASK); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, + OV7670_COM8_AWB); + if (qvga) { /* QVGA from ov7670.c by + * Jonathan Corbet */ + xstart = 164; + xend = 28; + ystart = 14; + yend = 494; + } else { /* VGA */ + xstart = 158; + xend = 14; + ystart = 10; + yend = 490; + } + /* OV7670 hardware window registers are split across + * multiple locations */ + i2c_w(sd, OV7670_REG_HSTART, xstart >> 3); + i2c_w(sd, OV7670_REG_HSTOP, xend >> 3); + v = i2c_r(sd, OV7670_REG_HREF); + v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_HREF, v); + + i2c_w(sd, OV7670_REG_VSTART, ystart >> 2); + i2c_w(sd, OV7670_REG_VSTOP, yend >> 2); + v = i2c_r(sd, OV7670_REG_VREF); + v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03); + msleep(10); /* need to sleep between read and write to + * same reg! */ + i2c_w(sd, OV7670_REG_VREF, v); break; case SEN_OV6620: + i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */ + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ + break; case SEN_OV6630: case SEN_OV66308AF: i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20); + i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */ break; default: return -EINVAL; } - /******** Palette-specific regs ********/ - - /* The OV518 needs special treatment. Although both the OV518 - * and the OV6630 support a 16-bit video bus, only the 8 bit Y - * bus is actually used. The UV bus is tied to ground. - * Therefore, the OV6630 needs to be in 8-bit multiplexed - * output mode */ - - /* OV7640 is 8-bit only */ - - if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF && - sd->sensor != SEN_OV7640) - i2c_w_mask(sd, 0x13, 0x00, 0x20); - /******** Clock programming ********/ i2c_w(sd, 0x11, sd->clockdiv); - /******** Special Features ********/ -/* no evidence this is possible with OV7670, either */ - /* Test Pattern */ - if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670) - i2c_w_mask(sd, 0x12, 0x00, 0x02); - - /* Enable auto white balance */ - if (sd->sensor == SEN_OV7670) - i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB, - OV7670_COM8_AWB); - else - i2c_w_mask(sd, 0x12, 0x04, 0x04); - - /* This will go away as soon as ov51x_mode_init_sensor_regs() */ - /* is fully tested. */ - /* 7620/6620/6630? don't have register 0x35, so play it safe */ - if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) { - if (!qvga) - i2c_w(sd, 0x35, 0x9e); - else - i2c_w(sd, 0x35, 0x1e); - } return 0; } @@ -2659,8 +3751,12 @@ static int set_ov_sensor_window(struct sd *sd) struct gspca_dev *gspca_dev; int qvga, crop; int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale; - int ret, hstart, hstop, vstop, vstart; - __u8 v; + int ret; + + /* mode setup is fully handled in mode_init_ov_sensor_regs for these */ + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610 || + sd->sensor == SEN_OV7670) + return mode_init_ov_sensor_regs(sd); gspca_dev = &sd->gspca_dev; qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1; @@ -2708,11 +3804,6 @@ static int set_ov_sensor_window(struct sd *sd) hwebase = 0x1a; vwsbase = vwebase = 0x03; break; - case SEN_OV7670: - /*handling of OV7670 hardware sensor start and stop values - * is very odd, compared to the other OV sensors */ - vwsbase = vwebase = hwebase = hwsbase = 0x00; - break; default: return -EINVAL; } @@ -2753,58 +3844,11 @@ static int set_ov_sensor_window(struct sd *sd) if (ret < 0) return ret; - if (sd->sensor == SEN_OV8610) { - i2c_w_mask(sd, 0x2d, 0x05, 0x40); - /* old 0x95, new 0x05 from windrv 090403 */ - /* bits 5-7: reserved */ - i2c_w_mask(sd, 0x28, 0x20, 0x20); - /* bit 5: progressive mode on */ - } - - /* The below is wrong for OV7670s because their window registers - * only store the high bits in 0x17 to 0x1a */ - - /* SRH Use sd->max values instead of requested win values */ - /* SCS Since we're sticking with only the max hardware widths - * for a given mode */ - /* I can hard code this for OV7670s */ - /* Yes, these numbers do look odd, but they're tested and work! */ - if (sd->sensor == SEN_OV7670) { - if (qvga) { /* QVGA from ov7670.c by - * Jonathan Corbet */ - hstart = 164; - hstop = 28; - vstart = 14; - vstop = 494; - } else { /* VGA */ - hstart = 158; - hstop = 14; - vstart = 10; - vstop = 490; - } - /* OV7670 hardware window registers are split across - * multiple locations */ - i2c_w(sd, OV7670_REG_HSTART, hstart >> 3); - i2c_w(sd, OV7670_REG_HSTOP, hstop >> 3); - v = i2c_r(sd, OV7670_REG_HREF); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07); - msleep(10); /* need to sleep between read and write to - * same reg! */ - i2c_w(sd, OV7670_REG_HREF, v); + i2c_w(sd, 0x17, hwsbase); + i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale)); + i2c_w(sd, 0x19, vwsbase); + i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale)); - i2c_w(sd, OV7670_REG_VSTART, vstart >> 2); - i2c_w(sd, OV7670_REG_VSTOP, vstop >> 2); - v = i2c_r(sd, OV7670_REG_VREF); - v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03); - msleep(10); /* need to sleep between read and write to - * same reg! */ - i2c_w(sd, OV7670_REG_VREF, v); - } else { - i2c_w(sd, 0x17, hwsbase); - i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale)); - i2c_w(sd, 0x19, vwsbase); - i2c_w(sd, 0x1a, vwebase + (sd->gspca_dev.height >> vwscale)); - } return 0; } @@ -2814,6 +3858,10 @@ static int sd_start(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int ret = 0; + /* Default for most bridges, allow bridge_mode_init_regs to override */ + sd->sensor_width = sd->gspca_dev.width; + sd->sensor_height = sd->gspca_dev.height; + switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: @@ -2826,6 +3874,10 @@ static int sd_start(struct gspca_dev *gspca_dev) case BRIDGE_OV519: ret = ov519_mode_init_regs(sd); break; + /* case BRIDGE_OVFX2: nothing to do */ + case BRIDGE_W9968CF: + ret = w9968cf_mode_init_regs(sd); + break; } if (ret < 0) goto out; @@ -2859,10 +3911,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev) ov51x_led_control(sd, 0); } +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge == BRIDGE_W9968CF) + w9968cf_stop0(sd); +} + static void ov511_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *in, /* isoc packet */ - int len) /* iso packet length */ + u8 *in, /* isoc packet */ + int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -2893,11 +3952,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev, return; } /* Add 11 byte footer to frame, might be usefull */ - gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11); + gspca_frame_add(gspca_dev, LAST_PACKET, in, 11); return; } else { /* Frame start */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0); sd->packet_nr = 0; } } @@ -2906,12 +3965,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev, len--; /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len); + gspca_frame_add(gspca_dev, INTER_PACKET, in, len); } static void ov518_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -2919,8 +3977,8 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, /* A false positive here is likely, until OVT gives me * the definitive SOF/EOF format */ if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) { - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); sd->packet_nr = 0; } @@ -2944,12 +4002,11 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev, } /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void ov519_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { /* Header of ov519 is 16 bytes: @@ -2972,7 +4029,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, len -= HDRSZ; #undef HDRSZ if (data[0] == 0xff || data[1] == 0xd8) - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); else gspca_dev->last_packet_type = DISCARD_PACKET; @@ -2980,20 +4037,31 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev, case 0x51: /* end of frame */ if (data[9] != 0) gspca_dev->last_packet_type = DISCARD_PACKET; - gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); return; } } /* intermediate packet */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + /* A short read signals EOF */ + if (len < OVFX2_BULK_SIZE) { + gspca_frame_add(gspca_dev, LAST_PACKET, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); + return; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -3001,14 +4069,20 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, switch (sd->bridge) { case BRIDGE_OV511: case BRIDGE_OV511PLUS: - ov511_pkt_scan(gspca_dev, frame, data, len); + ov511_pkt_scan(gspca_dev, data, len); break; case BRIDGE_OV518: case BRIDGE_OV518PLUS: - ov518_pkt_scan(gspca_dev, frame, data, len); + ov518_pkt_scan(gspca_dev, data, len); break; case BRIDGE_OV519: - ov519_pkt_scan(gspca_dev, frame, data, len); + ov519_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_OVFX2: + ovfx2_pkt_scan(gspca_dev, data, len); + break; + case BRIDGE_W9968CF: + w9968cf_pkt_scan(gspca_dev, data, len); break; } } @@ -3124,7 +4198,8 @@ static void setcolors(struct gspca_dev *gspca_dev) static void setautobrightness(struct sd *sd) { - if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670) + if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670 || + sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) return; i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10); @@ -3132,6 +4207,9 @@ static void setautobrightness(struct sd *sd) static void setfreq(struct sd *sd) { + if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610) + return; + if (sd->sensor == SEN_OV7670) { switch (sd->freq) { case 0: /* Banding filter disabled */ @@ -3301,8 +4379,12 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) struct sd *sd = (struct sd *) gspca_dev; sd->freq = val; - if (gspca_dev->streaming) + if (gspca_dev->streaming) { setfreq(sd); + /* Ugly but necessary */ + if (sd->bridge == BRIDGE_W9968CF) + w9968cf_set_crop_window(sd); + } return 0; } @@ -3343,6 +4425,45 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, return -EINVAL; } +static int sd_get_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge != BRIDGE_W9968CF) + return -EINVAL; + + memset(jcomp, 0, sizeof *jcomp); + jcomp->quality = sd->quality; + jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT | + V4L2_JPEG_MARKER_DRI; + return 0; +} + +static int sd_set_jcomp(struct gspca_dev *gspca_dev, + struct v4l2_jpegcompression *jcomp) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->bridge != BRIDGE_W9968CF) + return -EINVAL; + + if (gspca_dev->streaming) + return -EBUSY; + + if (jcomp->quality < QUALITY_MIN) + sd->quality = QUALITY_MIN; + else if (jcomp->quality > QUALITY_MAX) + sd->quality = QUALITY_MAX; + else + sd->quality = jcomp->quality; + + /* Return resulting jcomp params to app */ + sd_get_jcomp(gspca_dev, jcomp); + + return 0; +} + /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, @@ -3352,18 +4473,23 @@ static const struct sd_desc sd_desc = { .init = sd_init, .start = sd_start, .stopN = sd_stopN, + .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .querymenu = sd_querymenu, + .get_jcomp = sd_get_jcomp, + .set_jcomp = sd_set_jcomp, }; /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF }, {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4061), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4064), .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, + {USB_DEVICE(0x041e, 0x4067), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x041e, 0x4068), .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED }, {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 }, @@ -3373,11 +4499,16 @@ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 }, {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 }, + {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 }, {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 }, {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS }, {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS }, {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS }, + {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 }, + {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF }, + {USB_DEVICE(0x8020, 0xEF04), .driver_info = BRIDGE_OVFX2 }, {} }; diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 4b528b3729118..4dbb882c83dcb 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -1,5 +1,6 @@ /* * ov534 gspca driver + * * Copyright (C) 2008 Antonio Ospite * Copyright (C) 2008 Jim Paris * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr @@ -8,6 +9,10 @@ * USB protocol reverse engineered by Jim Paris * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/ * + * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr + * PS3 Eye camera, brightness, contrast, hue, AWB control added + * by Max Thrun + * * 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, or @@ -51,16 +56,335 @@ struct sd { u16 last_fid; u8 frame_rate; + u8 brightness; + u8 contrast; + u8 gain; + u8 exposure; + u8 redblc; + u8 blueblc; + u8 hue; + u8 autogain; + u8 awb; + s8 sharpness; + u8 hflip; + u8 vflip; + u8 satur; + u8 lightfreq; + u8 sensor; #define SENSOR_OV772X 0 #define SENSOR_OV965X 1 }; /* V4L2 controls supported by the driver */ -static struct ctrl sd_ctrls[] = { +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls_ov772x[] = { + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BRIGHTNESS_77_DEF 20 + .default_value = BRIGHTNESS_77_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { /* 1 */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, +#define CONTRAST_77_DEF 37 + .default_value = CONTRAST_77_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { /* 2 */ + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Main Gain", + .minimum = 0, + .maximum = 63, + .step = 1, +#define GAIN_DEF 20 + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 255, + .step = 1, +#define EXPO_77_DEF 120 + .default_value = EXPO_77_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { /* 4 */ + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define RED_BALANCE_DEF 128 + .default_value = RED_BALANCE_DEF, + }, + .set = sd_setredblc, + .get = sd_getredblc, + }, + { /* 5 */ + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define BLUE_BALANCE_DEF 128 + .default_value = BLUE_BALANCE_DEF, + }, + .set = sd_setblueblc, + .get = sd_getblueblc, + }, + { /* 6 */ + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 255, + .step = 1, +#define HUE_DEF 143 + .default_value = HUE_DEF, + }, + .set = sd_sethue, + .get = sd_gethue, + }, + { /* 7 */ + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Autogain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_77_DEF 0 + .default_value = AUTOGAIN_77_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define AWB_77_IDX 8 + { /* 8 */ + { + .id = V4L2_CID_AUTO_WHITE_BALANCE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto White Balance", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AWB_DEF 0 + .default_value = AWB_DEF, + }, + .set = sd_setawb, + .get = sd_getawb, + }, + { /* 9 */ + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = 0, + .maximum = 63, + .step = 1, +#define SHARPNESS_77_DEF 0 + .default_value = SHARPNESS_77_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, + { /* 10 */ + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "HFlip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { /* 11 */ + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "VFlip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +}; +static struct ctrl sd_ctrls_ov965x[] = { + { /* 0 */ + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 15, + .step = 1, +#define BRIGHTNESS_96_DEF 7 + .default_value = BRIGHTNESS_96_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, + { /* 1 */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 15, + .step = 1, +#define CONTRAST_96_DEF 3 + .default_value = CONTRAST_96_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, + { /* 2 */ + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Autogain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_96_DEF 1 + .default_value = AUTOGAIN_96_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, +#define EXPO_96_IDX 3 + { /* 3 */ + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, + .maximum = 3, + .step = 1, +#define EXPO_96_DEF 0 + .default_value = EXPO_96_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { /* 4 */ + { + .id = V4L2_CID_SHARPNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Sharpness", + .minimum = -1, /* -1 = auto */ + .maximum = 4, + .step = 1, +#define SHARPNESS_96_DEF -1 + .default_value = SHARPNESS_96_DEF, + }, + .set = sd_setsharpness, + .get = sd_getsharpness, + }, + { /* 5 */ + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 4, + .step = 1, +#define SATUR_DEF 2 + .default_value = SATUR_DEF, + }, + .set = sd_setsatur, + .get = sd_getsatur, + }, + { + { + .id = V4L2_CID_POWER_LINE_FREQUENCY, + .type = V4L2_CTRL_TYPE_MENU, + .name = "Light frequency filter", + .minimum = 0, + .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ + .step = 1, +#define FREQ_DEF 0 + .default_value = FREQ_DEF, + }, + .set = sd_setfreq, + .get = sd_getfreq, + }, }; -static const struct v4l2_pix_format vga_yuyv_mode[] = { +static const struct v4l2_pix_format ov772x_mode[] = { + {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_SRGB, + .priv = 1}, {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE, .bytesperline = 640 * 2, .sizeimage = 640 * 480 * 2, @@ -68,20 +392,35 @@ static const struct v4l2_pix_format vga_yuyv_mode[] = { .priv = 0}, }; -static const struct v4l2_pix_format vga_jpeg_mode[] = { +static const struct v4l2_pix_format ov965x_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, .sizeimage = 320 * 240 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, - .priv = 1}, + .priv = 4}, {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 640, .sizeimage = 640 * 480 * 3 / 8 + 590, .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 3}, + {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 800, + .sizeimage = 800 * 600 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 2}, + {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1024, + .sizeimage = 1024 * 768 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 1}, + {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 1280, + .sizeimage = 1280 * 1024 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, .priv = 0}, }; -static const u8 bridge_init_ov722x[][2] = { +static const u8 bridge_init_ov772x[][2] = { { 0xc2, 0x0c }, { 0x88, 0xf8 }, { 0xc3, 0x69 }, @@ -122,6 +461,7 @@ static const u8 bridge_init_ov722x[][2] = { { 0x1d, 0x40 }, { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */ { 0x1d, 0x00 }, /* payload size */ + { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */ { 0x1d, 0x58 }, /* frame size */ { 0x1d, 0x00 }, /* frame size */ @@ -138,10 +478,20 @@ static const u8 bridge_init_ov722x[][2] = { { 0xc1, 0x3c }, { 0xc2, 0x0c }, }; - -static const u8 sensor_init_ov722x[][2] = { +static const u8 sensor_init_ov772x[][2] = { { 0x12, 0x80 }, { 0x11, 0x01 }, +/*fixme: better have a delay?*/ + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, + { 0x11, 0x01 }, { 0x3d, 0x03 }, { 0x17, 0x26 }, @@ -154,10 +504,10 @@ static const u8 sensor_init_ov722x[][2] = { { 0x65, 0x20 }, { 0x11, 0x01 }, { 0x42, 0x7f }, - { 0x63, 0xe0 }, + { 0x63, 0xaa }, /* AWB - was e0 */ { 0x64, 0xff }, { 0x66, 0x00 }, - { 0x13, 0xf0 }, + { 0x13, 0xf0 }, /* com8 */ { 0x0d, 0x41 }, { 0x0f, 0xc5 }, { 0x14, 0x11 }, @@ -170,7 +520,7 @@ static const u8 sensor_init_ov722x[][2] = { { 0x2a, 0x00 }, { 0x2b, 0x00 }, { 0x6b, 0xaa }, - { 0x13, 0xff }, + { 0x13, 0xff }, /* AWB */ { 0x90, 0x05 }, { 0x91, 0x01 }, @@ -218,9 +568,51 @@ static const u8 sensor_init_ov722x[][2] = { { 0x14, 0x41 }, { 0x0e, 0xcd }, { 0xac, 0xbf }, - { 0x8e, 0x00 }, + { 0x8e, 0x00 }, /* De-noise threshold */ { 0x0c, 0xd0 } }; +static const u8 bridge_start_ov772x_vga[][2] = { + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x02}, + {0x1d, 0x58}, + {0x1d, 0x00}, + {0xc0, 0x50}, + {0xc1, 0x3c}, +}; +static const u8 sensor_start_ov772x_vga[][2] = { + {0x12, 0x00}, + {0x17, 0x26}, + {0x18, 0xa0}, + {0x19, 0x07}, + {0x1a, 0xf0}, + {0x29, 0xa0}, + {0x2c, 0xf0}, + {0x65, 0x20}, +}; +static const u8 bridge_start_ov772x_qvga[][2] = { + {0x1c, 0x00}, + {0x1d, 0x40}, + {0x1d, 0x02}, + {0x1d, 0x00}, + {0x1d, 0x01}, + {0x1d, 0x4b}, + {0x1d, 0x00}, + {0xc0, 0x28}, + {0xc1, 0x1e}, +}; +static const u8 sensor_start_ov772x_qvga[][2] = { + {0x12, 0x40}, + {0x17, 0x3f}, + {0x18, 0x50}, + {0x19, 0x03}, + {0x1a, 0x78}, + {0x29, 0x50}, + {0x2c, 0x78}, + {0x65, 0x2f}, +}; static const u8 bridge_init_ov965x[][2] = { {0x88, 0xf8}, @@ -403,7 +795,7 @@ static const u8 sensor_init_ov965x[][2] = { {0xcb, 0xf0}, {0xcc, 0xd8}, {0xcd, 0xf1}, - {0x4f, 0x98}, + {0x4f, 0x98}, /* matrix */ {0x50, 0x98}, {0x51, 0x00}, {0x52, 0x28}, @@ -412,6 +804,7 @@ static const u8 sensor_init_ov965x[][2] = { {0x58, 0x1a}, {0xff, 0x41}, /* read 41, write ff 00 */ {0x41, 0x40}, /* com16 */ + {0xc5, 0x03}, /* 60 Hz banding filter */ {0x6a, 0x02}, /* 50 Hz banding filter */ @@ -455,8 +848,8 @@ static const u8 bridge_init_ov965x_2[][2] = { {0x52, 0x3c}, {0x53, 0x00}, {0x54, 0x00}, - {0x55, 0x00}, /* brightness */ - {0x57, 0x00}, /* contrast 2 */ + {0x55, 0x00}, + {0x57, 0x00}, {0x5c, 0x00}, {0x5a, 0xa0}, {0x5b, 0x78}, @@ -479,14 +872,16 @@ static const u8 sensor_init_ov965x_2[][2] = { {0xa3, 0x3e}, {0x2d, 0x00}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc0}, + {0x42, 0xc0}, /* com17 */ {0x2d, 0x00}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, + {0x42, 0xc1}, /* com17 */ +/* sharpness */ {0x3f, 0x01}, {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, - {0x4f, 0x98}, + {0x42, 0xc1}, /* com17 */ +/* saturation */ + {0x4f, 0x98}, /* matrix */ {0x50, 0x98}, {0x51, 0x00}, {0x52, 0x28}, @@ -495,14 +890,17 @@ static const u8 sensor_init_ov965x_2[][2] = { {0x58, 0x1a}, {0xff, 0x41}, /* read 41, write ff 00 */ {0x41, 0x40}, /* com16 */ +/* contrast */ {0x56, 0x40}, +/* brightness */ {0x55, 0x8f}, +/* expo */ {0x10, 0x25}, /* aech - exposure high bits */ {0xff, 0x13}, /* read 13, write ff 00 */ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ }; -static const u8 sensor_start_ov965x[][2] = { +static const u8 sensor_start_ov965x_1_vga[][2] = { /* same for qvga */ {0x12, 0x62}, /* com7 - 30fps VGA YUV */ {0x36, 0xfa}, /* aref3 */ {0x69, 0x0a}, /* hv */ @@ -523,10 +921,77 @@ static const u8 sensor_start_ov965x[][2] = { {0x1a, 0x3d}, /* vstop */ {0x32, 0xff}, /* href */ {0xc0, 0xaa}, - {} }; -static const u8 bridge_start_ov965x[][2] = { +static const u8 sensor_start_ov965x_1_svga[][2] = { + {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x0d}, /* com22 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 sensor_start_ov965x_1_xga[][2] = { + {0x12, 0x02}, /* com7 */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0xbd}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 sensor_start_ov965x_1_sxga[][2] = { + {0x12, 0x02}, /* com7 */ + {0x36, 0xf8}, /* aref3 */ + {0x69, 0x02}, /* hv */ + {0x8c, 0x89}, /* com22 */ + {0x14, 0x28}, /* com9 */ + {0x3e, 0x0c}, /* com14 */ + {0x41, 0x40}, /* com16 */ + {0x72, 0x00}, + {0x73, 0x01}, + {0x74, 0x3a}, + {0x75, 0x35}, + {0x76, 0x01}, + {0xc7, 0x80}, /* com24 */ + {0x03, 0x1b}, /* vref */ + {0x17, 0x1d}, /* hstart */ + {0x18, 0x02}, /* hstop */ + {0x19, 0x01}, /* vstrt */ + {0x1a, 0x81}, /* vstop */ + {0x32, 0xff}, /* href */ + {0xc0, 0xe2}, +}; + +static const u8 bridge_start_ov965x_qvga[][2] = { {0x94, 0xaa}, {0xf1, 0x60}, {0xe5, 0x04}, @@ -535,10 +1000,34 @@ static const u8 bridge_start_ov965x[][2] = { {0x8c, 0x00}, {0x8d, 0x1c}, {0x34, 0x05}, - {} + + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x50, 0x00}, + {0x51, 0xa0}, + {0x52, 0x78}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x00}, + {0x57, 0x00}, + {0x5c, 0x00}, + {0x5a, 0x50}, + {0x5b, 0x3c}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, }; static const u8 bridge_start_ov965x_vga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0x50}, + {0xc1, 0x3c}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, {0xc2, 0x0c}, {0xc3, 0xf9}, {0xda, 0x01}, @@ -555,30 +1044,98 @@ static const u8 bridge_start_ov965x_vga[][2] = { {0x35, 0x02}, {0xd9, 0x10}, {0x94, 0x11}, - {} }; -static const u8 bridge_start_ov965x_cif[][2] = { +static const u8 bridge_start_ov965x_svga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, {0xc2, 0x4c}, {0xc3, 0xf9}, - {0xda, 0x00}, {0x50, 0x00}, - {0x51, 0xa0}, - {0x52, 0x78}, + {0x51, 0x40}, + {0x52, 0x00}, {0x53, 0x00}, {0x54, 0x00}, - {0x55, 0x00}, + {0x55, 0x88}, {0x57, 0x00}, {0x5c, 0x00}, - {0x5a, 0x50}, - {0x5b, 0x3c}, + {0x5a, 0xc8}, + {0x5b, 0x96}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0xda, 0x00}, + {0x94, 0x11}, +}; + +static const u8 bridge_start_ov965x_xga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x4c}, + {0xc3, 0xf9}, + {0x50, 0x00}, + {0x51, 0x40}, + {0x52, 0x00}, + {0x53, 0x00}, + {0x54, 0x00}, + {0x55, 0x88}, + {0x57, 0x00}, + {0x5c, 0x01}, + {0x5a, 0x00}, + {0x5b, 0xc0}, {0x35, 0x02}, {0xd9, 0x10}, + {0xda, 0x01}, {0x94, 0x11}, - {} }; -static const u8 sensor_start_ov965x_vga[][2] = { +static const u8 bridge_start_ov965x_sxga[][2] = { + {0x94, 0xaa}, + {0xf1, 0x60}, + {0xe5, 0x04}, + {0xc0, 0xa0}, + {0xc1, 0x80}, + {0x8c, 0x00}, + {0x8d, 0x1c}, + {0x34, 0x05}, + {0xc2, 0x0c}, + {0xc3, 0xf9}, + {0xda, 0x00}, + {0x35, 0x02}, + {0xd9, 0x10}, + {0x94, 0x11}, +}; + +static const u8 sensor_start_ov965x_2_qvga[][2] = { + {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x02}, /* 50 Hz banding filter */ + {0xc5, 0x03}, /* 60 Hz banding filter */ + {0xa2, 0x96}, /* bd50 */ + {0xa3, 0x7d}, /* bd60 */ + + {0xff, 0x13}, /* read 13, write ff 00 */ + {0x13, 0xe7}, + {0x3a, 0x80}, /* tslb - yuyv */ +}; + +static const u8 sensor_start_ov965x_2_vga[][2] = { {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ @@ -592,35 +1149,36 @@ static const u8 sensor_start_ov965x_vga[][2] = { {0xa3, 0x3e}, /* bd60 */ {0x2d, 0x00}, /* advfl */ - {} }; -static const u8 sensor_start_ov965x_cif[][2] = { - {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */ +static const u8 sensor_start_ov965x_2_svga[][2] = { /* same for xga */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ {0x1e, 0x04}, /* mvfp */ {0x13, 0xe0}, /* com8 */ {0x00, 0x00}, {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ {0x11, 0x01}, /* clkrc */ {0x6b, 0x5a}, /* dblv */ - {0x6a, 0x02}, /* 50 Hz banding filter */ - {0xc5, 0x03}, /* 60 Hz banding filter */ - {0xa2, 0x96}, /* bd50 */ - {0xa3, 0x7d}, /* bd60 */ - - {0xff, 0x13}, /* read 13, write ff 00 */ - {0x13, 0xe7}, - {0x3a, 0x80}, /* tslb - yuyv */ - {} + {0x6a, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ }; -static const u8 sensor_start_ov965x_2[][2] = { - {0xff, 0x42}, /* read 42, write ff 00 */ - {0x42, 0xc1}, /* com17 - 50 Hz filter */ - {} +static const u8 sensor_start_ov965x_2_sxga[][2] = { + {0x13, 0xe0}, /* com8 */ + {0x00, 0x00}, + {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */ + {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */ + {0x1e, 0x04}, /* mvfp */ + {0x11, 0x01}, /* clkrc */ + {0x6b, 0x5a}, /* dblv */ + {0x6a, 0x0c}, /* 50 Hz banding filter */ + {0xc5, 0x0f}, /* 60 Hz banding filter */ + {0xa2, 0x4e}, /* bd50 */ + {0xa3, 0x41}, /* bd60 */ }; - static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val) { struct usb_device *udev = gspca_dev->dev; @@ -753,76 +1311,390 @@ static void sccb_w_array(struct gspca_dev *gspca_dev, } } -/* set framerate */ -static void ov534_set_frame_rate(struct gspca_dev *gspca_dev) +/* ov772x specific controls */ +static void set_frame_rate(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i; + struct rate_s { + u8 fps; + u8 r11; + u8 r0d; + u8 re5; + }; + const struct rate_s *r; + static const struct rate_s rate_0[] = { /* 640x480 */ + {60, 0x01, 0xc1, 0x04}, + {50, 0x01, 0x41, 0x02}, + {40, 0x02, 0xc1, 0x04}, + {30, 0x04, 0x81, 0x02}, + {15, 0x03, 0x41, 0x04}, + }; + static const struct rate_s rate_1[] = { /* 320x240 */ + {125, 0x02, 0x81, 0x02}, + {100, 0x02, 0xc1, 0x04}, + {75, 0x03, 0xc1, 0x04}, + {60, 0x04, 0xc1, 0x04}, + {50, 0x02, 0x41, 0x04}, + {40, 0x03, 0x41, 0x04}, + {30, 0x04, 0x41, 0x04}, + }; + + if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) { + r = rate_0; + i = ARRAY_SIZE(rate_0); + } else { + r = rate_1; + i = ARRAY_SIZE(rate_1); + } + while (--i > 0) { + if (sd->frame_rate >= r->fps) + break; + r++; + } + + sccb_reg_write(gspca_dev, 0x11, r->r11); + sccb_reg_write(gspca_dev, 0x0d, r->r0d); + ov534_reg_write(gspca_dev, 0xe5, r->re5); + + PDEBUG(D_PROBE, "frame_rate: %d", r->fps); +} + +static void setbrightness_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x9B, sd->brightness); +} + +static void setcontrast_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x9C, sd->contrast); +} + +static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int fr = sd->frame_rate; + u8 val; - switch (fr) { - case 50: - sccb_reg_write(gspca_dev, 0x11, 0x01); - sccb_reg_write(gspca_dev, 0x0d, 0x41); - ov534_reg_write(gspca_dev, 0xe5, 0x02); + val = sd->gain; + switch (val & 0x30) { + case 0x00: + val &= 0x0f; break; - case 40: - sccb_reg_write(gspca_dev, 0x11, 0x02); - sccb_reg_write(gspca_dev, 0x0d, 0xc1); - ov534_reg_write(gspca_dev, 0xe5, 0x04); + case 0x10: + val &= 0x0f; + val |= 0x30; break; -/* case 30: */ - default: - fr = 30; - sccb_reg_write(gspca_dev, 0x11, 0x04); - sccb_reg_write(gspca_dev, 0x0d, 0x81); - ov534_reg_write(gspca_dev, 0xe5, 0x02); + case 0x20: + val &= 0x0f; + val |= 0x70; break; - case 15: - sccb_reg_write(gspca_dev, 0x11, 0x03); - sccb_reg_write(gspca_dev, 0x0d, 0x41); - ov534_reg_write(gspca_dev, 0xe5, 0x04); + default: +/* case 0x30: */ + val &= 0x0f; + val |= 0xf0; break; } + sccb_reg_write(gspca_dev, 0x00, val); +} + +static void setexposure_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; - sd->frame_rate = fr; - PDEBUG(D_PROBE, "frame_rate: %d", fr); + val = sd->exposure; + sccb_reg_write(gspca_dev, 0x08, val >> 7); + sccb_reg_write(gspca_dev, 0x10, val << 1); } -/* this function is called at probe time */ -static int sd_config(struct gspca_dev *gspca_dev, - const struct usb_device_id *id) +static void setredblc(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - sd->sensor = id->driver_info; + sccb_reg_write(gspca_dev, 0x43, sd->redblc); +} - cam = &gspca_dev->cam; +static void setblueblc(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; - if (sd->sensor == SENSOR_OV772X) { - cam->cam_mode = vga_yuyv_mode; - cam->nmodes = ARRAY_SIZE(vga_yuyv_mode); + sccb_reg_write(gspca_dev, 0x42, sd->blueblc); +} - cam->bulk = 1; - cam->bulk_size = 16384; - cam->bulk_nurbs = 2; - } else { /* ov965x */ - cam->cam_mode = vga_jpeg_mode; - cam->nmodes = ARRAY_SIZE(vga_jpeg_mode); - } +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; - return 0; + sccb_reg_write(gspca_dev, 0x01, sd->hue); } -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) +static void setautogain_77(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - u16 sensor_id; - static const u8 sensor_addr[2] = { - 0x42, /* 0 SENSOR_OV772X */ - 0x60, /* 1 SENSOR_OV965X */ - }; + + if (sd->autogain) { + sccb_reg_write(gspca_dev, 0x13, 0xf7); /* AGC,AEC,AWB ON */ + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) | 0x03); + } else { + sccb_reg_write(gspca_dev, 0x13, 0xf0); /* AGC,AEC,AWB OFF */ + sccb_reg_write(gspca_dev, 0x64, + sccb_reg_read(gspca_dev, 0x64) & 0xfc); + } +} + +static void setawb(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->awb) + sccb_reg_write(gspca_dev, 0x63, 0xe0); /* AWB on */ + else + sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */ +} + +static void setsharpness_77(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->sharpness; + sccb_reg_write(gspca_dev, 0x91, val); /* vga noise */ + sccb_reg_write(gspca_dev, 0x8e, val); /* qvga noise */ +} + +static void sethflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->hflip == 0) + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) | 0x40); + else + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) & 0xbf); +} + +static void setvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->vflip == 0) + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) | 0x80); + else + sccb_reg_write(gspca_dev, 0x0c, + sccb_reg_read(gspca_dev, 0x0c) & 0x7f); +} + +/* ov965x specific controls */ +static void setbrightness_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->brightness; + if (val < 8) + val = 15 - val; /* f .. 8 */ + else + val = val - 8; /* 0 .. 7 */ + sccb_reg_write(gspca_dev, 0x55, /* brtn - brightness adjustment */ + 0x0f | (val << 4)); +} + +static void setcontrast_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sccb_reg_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */ + sd->contrast << 4); +} + +static void setexposure_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e}; + + sccb_reg_write(gspca_dev, 0x10, /* aec[9:2] */ + expo[sd->exposure]); + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x13, val); + val = sccb_reg_read(gspca_dev, 0xa1); /* aech */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */ +} + +static void setsharpness_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sd->sharpness; + if (val < 0) { /* auto */ + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x42, val | 0x40); + /* Edge enhancement strength auto adjust */ + return; + } + if (val != 0) + val = 1 << (val - 1); + sccb_reg_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */ + val); + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x42, val & 0xbf); +} + +static void setautogain_96(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + +/*fixme: should adjust agc/awb/aec by different controls */ + val = sd->autogain; + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->autogain) + val |= 0x05; /* agc & aec */ + else + val &= 0xfa; + sccb_reg_write(gspca_dev, 0x13, val); +} + +static void setsatur(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val1, val2, val3; + static const u8 matrix[5][2] = { + {0x14, 0x38}, + {0x1e, 0x54}, + {0x28, 0x70}, + {0x32, 0x8c}, + {0x48, 0x90} + }; + + val1 = matrix[sd->satur][0]; + val2 = matrix[sd->satur][1]; + val3 = val1 + val2; + sccb_reg_write(gspca_dev, 0x4f, val3); /* matrix coeff */ + sccb_reg_write(gspca_dev, 0x50, val3); + sccb_reg_write(gspca_dev, 0x51, 0x00); + sccb_reg_write(gspca_dev, 0x52, val1); + sccb_reg_write(gspca_dev, 0x53, val2); + sccb_reg_write(gspca_dev, 0x54, val3); + sccb_reg_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */ + val1 = sccb_reg_read(gspca_dev, 0x41); /* com16 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + sccb_reg_write(gspca_dev, 0x41, val1); +} + +static void setfreq(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u8 val; + + val = sccb_reg_read(gspca_dev, 0x13); /* com8 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->lightfreq == 0) { + sccb_reg_write(gspca_dev, 0x13, val & 0xdf); + return; + } + sccb_reg_write(gspca_dev, 0x13, val | 0x20); + + val = sccb_reg_read(gspca_dev, 0x42); /* com17 */ + sccb_reg_write(gspca_dev, 0xff, 0x00); + if (sd->lightfreq == 1) + val |= 0x01; + else + val &= 0xfe; + sccb_reg_write(gspca_dev, 0x42, val); +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + sd->sensor = id->driver_info; + + cam = &gspca_dev->cam; + + if (sd->sensor == SENSOR_OV772X) { + cam->cam_mode = ov772x_mode; + cam->nmodes = ARRAY_SIZE(ov772x_mode); + + cam->bulk = 1; + cam->bulk_size = 16384; + cam->bulk_nurbs = 2; + } else { /* ov965x */ + cam->cam_mode = ov965x_mode; + cam->nmodes = ARRAY_SIZE(ov965x_mode); + } + + sd->frame_rate = 30; + + if (sd->sensor == SENSOR_OV772X) { + sd->brightness = BRIGHTNESS_77_DEF; + sd->contrast = CONTRAST_77_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPO_77_DEF; + sd->redblc = RED_BALANCE_DEF; + sd->blueblc = BLUE_BALANCE_DEF; + sd->hue = HUE_DEF; +#if AUTOGAIN_77_DEF != 0 + sd->autogain = AUTOGAIN_77_DEF; +#else + gspca_dev->ctrl_inac |= (1 << AWB_77_IDX); +#endif +#if AWB_DEF != 0 + sd->awb = AWB_DEF +#endif +#if SHARPNESS_77_DEF != 0 + sd->sharpness = SHARPNESS_77_DEF; +#endif +#if HFLIP_DEF != 0 + sd->hflip = HFLIP_DEF; +#endif +#if VFLIP_DEF != 0 + sd->vflip = VFLIP_DEF; +#endif + } else { + sd->brightness = BRIGHTNESS_96_DEF; + sd->contrast = CONTRAST_96_DEF; +#if AUTOGAIN_96_DEF != 0 + sd->autogain = AUTOGAIN_96_DEF; + gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX); +#endif +#if EXPO_96_DEF != 0 + sd->exposure = EXPO_96_DEF; +#endif +#if SHARPNESS_96_DEF != 0 + sd->sharpness = SHARPNESS_96_DEF; +#endif + sd->satur = SATUR_DEF; + sd->lightfreq = FREQ_DEF; + } + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + u16 sensor_id; + static const u8 sensor_addr[2] = { + 0x42, /* 0 SENSOR_OV772X */ + 0x60, /* 1 SENSOR_OV965X */ + }; /* reset bridge */ ov534_reg_write(gspca_dev, 0xe7, 0x3a); @@ -847,14 +1719,14 @@ static int sd_init(struct gspca_dev *gspca_dev) /* initialize */ switch (sd->sensor) { case SENSOR_OV772X: - reg_w_array(gspca_dev, bridge_init_ov722x, - ARRAY_SIZE(bridge_init_ov722x)); + reg_w_array(gspca_dev, bridge_init_ov772x, + ARRAY_SIZE(bridge_init_ov772x)); ov534_set_led(gspca_dev, 1); - sccb_w_array(gspca_dev, sensor_init_ov722x, - ARRAY_SIZE(sensor_init_ov722x)); + sccb_w_array(gspca_dev, sensor_init_ov772x, + ARRAY_SIZE(sensor_init_ov772x)); ov534_reg_write(gspca_dev, 0xe0, 0x09); ov534_set_led(gspca_dev, 0); - ov534_set_frame_rate(gspca_dev); + set_frame_rate(gspca_dev); break; default: /* case SENSOR_OV965X: */ @@ -875,60 +1747,115 @@ static int sd_init(struct gspca_dev *gspca_dev) return 0; } -static int sd_start(struct gspca_dev *gspca_dev) +static int sd_start_ov772x(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; int mode; - switch (sd->sensor) { - case SENSOR_OV772X: - ov534_set_led(gspca_dev, 1); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - break; - default: -/* case SENSOR_OV965X: */ - - sccb_w_array(gspca_dev, sensor_start_ov965x, - ARRAY_SIZE(sensor_start_ov965x)); - reg_w_array(gspca_dev, bridge_start_ov965x, - ARRAY_SIZE(bridge_start_ov965x)); - mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; - if (mode != 0) { /* 320x240 */ - reg_w_array(gspca_dev, bridge_start_ov965x_cif, - ARRAY_SIZE(bridge_start_ov965x_cif)); - sccb_w_array(gspca_dev, sensor_start_ov965x_cif, - ARRAY_SIZE(sensor_start_ov965x_cif)); - } else { /* 640x480 */ - reg_w_array(gspca_dev, bridge_start_ov965x_vga, - ARRAY_SIZE(bridge_start_ov965x_vga)); - sccb_w_array(gspca_dev, sensor_start_ov965x_vga, - ARRAY_SIZE(sensor_start_ov965x_vga)); - } - sccb_w_array(gspca_dev, sensor_start_ov965x_2, - ARRAY_SIZE(sensor_start_ov965x_2)); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - ov534_reg_write(gspca_dev, 0xe0, 0x00); - ov534_set_led(gspca_dev, 1); + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + if (mode != 0) { /* 320x240 */ + reg_w_array(gspca_dev, bridge_start_ov772x_qvga, + ARRAY_SIZE(bridge_start_ov772x_qvga)); + sccb_w_array(gspca_dev, sensor_start_ov772x_qvga, + ARRAY_SIZE(sensor_start_ov772x_qvga)); + } else { /* 640x480 */ + reg_w_array(gspca_dev, bridge_start_ov772x_vga, + ARRAY_SIZE(bridge_start_ov772x_vga)); + sccb_w_array(gspca_dev, sensor_start_ov772x_vga, + ARRAY_SIZE(sensor_start_ov772x_vga)); } + set_frame_rate(gspca_dev); + + setautogain_77(gspca_dev); + setawb(gspca_dev); + setgain(gspca_dev); + setredblc(gspca_dev); + setblueblc(gspca_dev); + sethue(gspca_dev); + setexposure_77(gspca_dev); + setbrightness_77(gspca_dev); + setcontrast_77(gspca_dev); + setsharpness_77(gspca_dev); + setvflip(gspca_dev); + sethflip(gspca_dev); + + ov534_set_led(gspca_dev, 1); + ov534_reg_write(gspca_dev, 0xe0, 0x00); return 0; } -static void sd_stopN(struct gspca_dev *gspca_dev) +static int sd_start_ov965x(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; + int mode; - switch (sd->sensor) { - case SENSOR_OV772X: - ov534_reg_write(gspca_dev, 0xe0, 0x09); - ov534_set_led(gspca_dev, 0); - break; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; + switch (mode) { default: -/* case SENSOR_OV965X: */ - ov534_reg_write(gspca_dev, 0xe0, 0x01); - ov534_set_led(gspca_dev, 0); - ov534_reg_write(gspca_dev, 0xe0, 0x00); +/* case 4: * 320x240 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga, + ARRAY_SIZE(sensor_start_ov965x_1_vga)); + reg_w_array(gspca_dev, bridge_start_ov965x_qvga, + ARRAY_SIZE(bridge_start_ov965x_qvga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_qvga, + ARRAY_SIZE(sensor_start_ov965x_2_qvga)); + break; + case 3: /* 640x480 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_vga, + ARRAY_SIZE(sensor_start_ov965x_1_vga)); + reg_w_array(gspca_dev, bridge_start_ov965x_vga, + ARRAY_SIZE(bridge_start_ov965x_vga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_vga, + ARRAY_SIZE(sensor_start_ov965x_2_vga)); + break; + case 2: /* 800x600 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_svga, + ARRAY_SIZE(sensor_start_ov965x_1_svga)); + reg_w_array(gspca_dev, bridge_start_ov965x_svga, + ARRAY_SIZE(bridge_start_ov965x_svga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga, + ARRAY_SIZE(sensor_start_ov965x_2_svga)); + break; + case 1: /* 1024x768 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_xga, + ARRAY_SIZE(sensor_start_ov965x_1_xga)); + reg_w_array(gspca_dev, bridge_start_ov965x_xga, + ARRAY_SIZE(bridge_start_ov965x_xga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_svga, + ARRAY_SIZE(sensor_start_ov965x_2_svga)); + break; + case 0: /* 1280x1024 */ + sccb_w_array(gspca_dev, sensor_start_ov965x_1_sxga, + ARRAY_SIZE(sensor_start_ov965x_1_sxga)); + reg_w_array(gspca_dev, bridge_start_ov965x_sxga, + ARRAY_SIZE(bridge_start_ov965x_sxga)); + sccb_w_array(gspca_dev, sensor_start_ov965x_2_sxga, + ARRAY_SIZE(sensor_start_ov965x_2_sxga)); break; } + setfreq(gspca_dev); + setautogain_96(gspca_dev); + setbrightness_96(gspca_dev); + setcontrast_96(gspca_dev); + setexposure_96(gspca_dev); + setsharpness_96(gspca_dev); + setsatur(gspca_dev); + + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_reg_write(gspca_dev, 0xe0, 0x00); + ov534_set_led(gspca_dev, 1); + return 0; +} + +static void sd_stopN_ov772x(struct gspca_dev *gspca_dev) +{ + ov534_reg_write(gspca_dev, 0xe0, 0x09); + ov534_set_led(gspca_dev, 0); +} + +static void sd_stopN_ov965x(struct gspca_dev *gspca_dev) +{ + ov534_reg_write(gspca_dev, 0xe0, 0x01); + ov534_set_led(gspca_dev, 0); + ov534_reg_write(gspca_dev, 0xe0, 0x00); } /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */ @@ -941,8 +1868,8 @@ static void sd_stopN(struct gspca_dev *gspca_dev) #define UVC_STREAM_EOF (1 << 1) #define UVC_STREAM_FID (1 << 0) -static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, - __u8 *data, int len) +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; __u32 this_pts; @@ -983,32 +1910,30 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, /* If PTS or FID has changed, start a new frame. */ if (this_pts != sd->last_pts || this_fid != sd->last_fid) { if (gspca_dev->last_packet_type == INTER_PACKET) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, frame, - NULL, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); sd->last_pts = this_pts; sd->last_fid = this_fid; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data + 12, len - 12); /* If this packet is marked as EOF, end the frame */ } else if (data[1] & UVC_STREAM_EOF) { sd->last_pts = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data + 12, len - 12); + gspca_frame_add(gspca_dev, LAST_PACKET, + data + 12, len - 12); } else { /* Add the data from this payload */ - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data + 12, len - 12); + gspca_frame_add(gspca_dev, INTER_PACKET, + data + 12, len - 12); } - /* Done this payload */ goto scan_next; discard: /* Discard data until a new frame starts. */ - gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0); + gspca_dev->last_packet_type = DISCARD_PACKET; scan_next: remaining_len -= len; @@ -1016,6 +1941,291 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame, } while (remaining_len > 0); } +/* controls */ +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setexposure_77(gspca_dev); + else + setexposure_96(gspca_dev); + } + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setbrightness_77(gspca_dev); + else + setbrightness_96(gspca_dev); + } + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setcontrast_77(gspca_dev); + else + setcontrast_96(gspca_dev); + } + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->satur = val; + if (gspca_dev->streaming) + setsatur(gspca_dev); + return 0; +} + +static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->satur; + return 0; +} +static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->lightfreq = val; + if (gspca_dev->streaming) + setfreq(gspca_dev); + return 0; +} + +static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->lightfreq; + return 0; +} + +static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->redblc = val; + if (gspca_dev->streaming) + setredblc(gspca_dev); + return 0; +} + +static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->redblc; + return 0; +} + +static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->blueblc = val; + if (gspca_dev->streaming) + setblueblc(gspca_dev); + return 0; +} + +static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blueblc; + return 0; +} + +static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hue = val; + if (gspca_dev->streaming) + sethue(gspca_dev); + return 0; +} + +static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hue; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) { + + /* the auto white balance control works only + * when auto gain is set */ + if (val) + gspca_dev->ctrl_inac &= ~(1 << AWB_77_IDX); + else + gspca_dev->ctrl_inac |= (1 << AWB_77_IDX); + setautogain_77(gspca_dev); + } else { + if (val) + gspca_dev->ctrl_inac |= (1 << EXPO_96_IDX); + else + gspca_dev->ctrl_inac &= ~(1 << EXPO_96_IDX); + setautogain_96(gspca_dev); + } + } + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->awb = val; + if (gspca_dev->streaming) + setawb(gspca_dev); + return 0; +} + +static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->awb; + return 0; +} + +static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->sharpness = val; + if (gspca_dev->streaming) { + if (sd->sensor == SENSOR_OV772X) + setsharpness_77(gspca_dev); + else + setsharpness_96(gspca_dev); + } + return 0; +} + +static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->sharpness; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + setvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + /* get stream parameters (framerate) */ static int sd_get_streamparm(struct gspca_dev *gspca_dev, struct v4l2_streamparm *parm) @@ -1047,7 +2257,8 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev, /* Set requested framerate */ sd->frame_rate = tpf->denominator / tpf->numerator; - ov534_set_frame_rate(gspca_dev); + if (gspca_dev->streaming && sd->sensor == SENSOR_OV772X) + set_frame_rate(gspca_dev); /* Return the actual framerate */ tpf->numerator = 1; @@ -1056,20 +2267,53 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev, return 0; } +static int sd_querymenu(struct gspca_dev *gspca_dev, + struct v4l2_querymenu *menu) +{ + switch (menu->id) { + case V4L2_CID_POWER_LINE_FREQUENCY: + switch (menu->index) { + case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */ + strcpy((char *) menu->name, "NoFliker"); + return 0; + case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */ + strcpy((char *) menu->name, "50 Hz"); + return 0; + case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */ + strcpy((char *) menu->name, "60 Hz"); + return 0; + } + break; + } + return -EINVAL; +} + /* sub-driver description */ -static const struct sd_desc sd_desc = { +static const struct sd_desc sd_desc_ov772x = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), + .ctrls = sd_ctrls_ov772x, + .nctrls = ARRAY_SIZE(sd_ctrls_ov772x), .config = sd_config, .init = sd_init, - .start = sd_start, - .stopN = sd_stopN, + .start = sd_start_ov772x, + .stopN = sd_stopN_ov772x, .pkt_scan = sd_pkt_scan, .get_streamparm = sd_get_streamparm, .set_streamparm = sd_set_streamparm, }; +static const struct sd_desc sd_desc_ov965x = { + .name = MODULE_NAME, + .ctrls = sd_ctrls_ov965x, + .nctrls = ARRAY_SIZE(sd_ctrls_ov965x), + .config = sd_config, + .init = sd_init, + .start = sd_start_ov965x, + .stopN = sd_stopN_ov965x, + .pkt_scan = sd_pkt_scan, + .querymenu = sd_querymenu, +}; + /* -- module initialisation -- */ static const __devinitdata struct usb_device_id device_table[] = { {USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X}, @@ -1082,8 +2326,12 @@ MODULE_DEVICE_TABLE(usb, device_table); /* -- device connect -- */ static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { - return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), - THIS_MODULE); + return gspca_dev_probe(intf, id, + id->driver_info == SENSOR_OV772X + ? &sd_desc_ov772x + : &sd_desc_ov965x, + sizeof(struct sd), + THIS_MODULE); } static struct usb_driver sd_driver = { @@ -1101,6 +2349,7 @@ static struct usb_driver sd_driver = { static int __init sd_mod_init(void) { int ret; + ret = usb_register(&sd_driver); if (ret < 0) return ret; diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 96659433d2488..4706a823add0b 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -337,14 +337,13 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { int n; @@ -354,10 +353,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, n -= sizeof pac_sof_marker; else n = 0; - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, n); + gspca_frame_add(gspca_dev, LAST_PACKET, + data, n); sd->header_read = 0; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0); len -= sof - data; data = sof; } @@ -381,7 +380,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, sd->header_read = 11; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c new file mode 100644 index 0000000000000..74acceea8094e --- /dev/null +++ b/drivers/media/video/gspca/pac7302.c @@ -0,0 +1,1272 @@ +/* + * Pixart PAC7302 library + * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li + * + * V4L2 by Jean-Francois Moine + * + * Separated from Pixart PAC7311 library by Márton Németh + * + * 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, or + * any later version. + * + * 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 + */ + +/* Some documentation about various registers as determined by trial and error. + When the register addresses differ between the 7202 and the 7311 the 2 + different addresses are written as 7302addr/7311addr, when one of the 2 + addresses is a - sign that register description is not valid for the + matching IC. + + Register page 1: + + Address Description + -/0x08 Unknown compressor related, must always be 8 except when not + in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 ! + -/0x1b Auto white balance related, bit 0 is AWB enable (inverted) + bits 345 seem to toggle per color gains on/off (inverted) + 0x78 Global control, bit 6 controls the LED (inverted) + -/0x80 JPEG compression ratio ? Best not touched + + Register page 3/4: + + Address Description + 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on + the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + -/0x0f Master gain 1-245, low value = high gain + 0x10/- Master gain 0-31 + -/0x10 Another gain 0-15, limited influence (1-2x gain I guess) + 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to + completely disable the analog amplification block. Set to 0x68 + for max gain, 0x14 for minimal gain. + + The registers are accessed in the following functions: + + Page | Register | Function + -----+------------+--------------------------------------------------- + 0 | 0x0f..0x20 | setcolors() + 0 | 0xa2..0xab | setbrightcont() + 0 | 0xc5 | setredbalance() + 0 | 0xc6 | setwhitebalance() + 0 | 0xc7 | setbluebalance() + 0 | 0xdc | setbrightcont(), setcolors() + 3 | 0x02 | setexposure() + 3 | 0x10 | setgain() + 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() + 3 | 0x21 | sethvflip() +*/ + +#define MODULE_NAME "pac7302" + +#include +#include "gspca.h" + +MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); +MODULE_DESCRIPTION("Pixart PAC7302"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor for pac7302 */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + + unsigned char brightness; + unsigned char contrast; + unsigned char colors; + unsigned char white_balance; + unsigned char red_balance; + unsigned char blue_balance; + unsigned char gain; + unsigned char exposure; + unsigned char autogain; + __u8 hflip; + __u8 vflip; + + u8 sof_read; + u8 autogain_ignore_frames; + + atomic_t avg_lum; +}; + +/* V4L2 controls supported by the driver */ +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); + +static struct ctrl sd_ctrls[] = { +/* This control is pac7302 only */ + { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, +#define BRIGHTNESS_MAX 0x20 + .maximum = BRIGHTNESS_MAX, + .step = 1, +#define BRIGHTNESS_DEF 0x10 + .default_value = BRIGHTNESS_DEF, + }, + .set = sd_setbrightness, + .get = sd_getbrightness, + }, +/* This control is for both the 7302 and the 7311 */ + { + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, +#define CONTRAST_MAX 255 + .maximum = CONTRAST_MAX, + .step = 1, +#define CONTRAST_DEF 127 + .default_value = CONTRAST_DEF, + }, + .set = sd_setcontrast, + .get = sd_getcontrast, + }, +/* This control is pac7302 only */ + { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, +#define COLOR_MAX 255 + .maximum = COLOR_MAX, + .step = 1, +#define COLOR_DEF 127 + .default_value = COLOR_DEF, + }, + .set = sd_setcolors, + .get = sd_getcolors, + }, + { + { + .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "White Balance", + .minimum = 0, + .maximum = 255, + .step = 1, +#define WHITEBALANCE_DEF 4 + .default_value = WHITEBALANCE_DEF, + }, + .set = sd_setwhitebalance, + .get = sd_getwhitebalance, + }, + { + { + .id = V4L2_CID_RED_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Red", + .minimum = 0, + .maximum = 3, + .step = 1, +#define REDBALANCE_DEF 1 + .default_value = REDBALANCE_DEF, + }, + .set = sd_setredbalance, + .get = sd_getredbalance, + }, + { + { + .id = V4L2_CID_BLUE_BALANCE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Blue", + .minimum = 0, + .maximum = 3, + .step = 1, +#define BLUEBALANCE_DEF 1 + .default_value = BLUEBALANCE_DEF, + }, + .set = sd_setbluebalance, + .get = sd_getbluebalance, + }, +/* All controls below are for both the 7302 and the 7311 */ + { + { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, +#define GAIN_MAX 255 + .maximum = GAIN_MAX, + .step = 1, +#define GAIN_DEF 127 +#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ + .default_value = GAIN_DEF, + }, + .set = sd_setgain, + .get = sd_getgain, + }, + { + { + .id = V4L2_CID_EXPOSURE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Exposure", + .minimum = 0, +#define EXPOSURE_MAX 255 + .maximum = EXPOSURE_MAX, + .step = 1, +#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */ +#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */ + .default_value = EXPOSURE_DEF, + }, + .set = sd_setexposure, + .get = sd_getexposure, + }, + { + { + .id = V4L2_CID_AUTOGAIN, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Auto Gain", + .minimum = 0, + .maximum = 1, + .step = 1, +#define AUTOGAIN_DEF 1 + .default_value = AUTOGAIN_DEF, + }, + .set = sd_setautogain, + .get = sd_getautogain, + }, + { + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mirror", + .minimum = 0, + .maximum = 1, + .step = 1, +#define HFLIP_DEF 0 + .default_value = HFLIP_DEF, + }, + .set = sd_sethflip, + .get = sd_gethflip, + }, + { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vflip", + .minimum = 0, + .maximum = 1, + .step = 1, +#define VFLIP_DEF 0 + .default_value = VFLIP_DEF, + }, + .set = sd_setvflip, + .get = sd_getvflip, + }, +}; + +static const struct v4l2_pix_format vga_mode[] = { + {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, + .bytesperline = 640, + .sizeimage = 640 * 480 * 3 / 8 + 590, + .colorspace = V4L2_COLORSPACE_JPEG, + .priv = 0}, +}; + +#define LOAD_PAGE3 255 +#define LOAD_PAGE4 254 +#define END_OF_SEQUENCE 0 + +/* pac 7302 */ +static const __u8 init_7302[] = { +/* index,value */ + 0xff, 0x01, /* page 1 */ + 0x78, 0x00, /* deactivate */ + 0xff, 0x01, + 0x78, 0x40, /* led off */ +}; +static const __u8 start_7302[] = { +/* index, len, [value]* */ + 0xff, 1, 0x00, /* page 0 */ + 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, + 0x00, 0x00, 0x00, 0x00, + 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, + 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, + 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, + 0x26, 2, 0xaa, 0xaa, + 0x2e, 1, 0x31, + 0x38, 1, 0x01, + 0x3a, 3, 0x14, 0xff, 0x5a, + 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, + 0x00, 0x54, 0x11, + 0x55, 1, 0x00, + 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, + 0x6b, 1, 0x00, + 0x6e, 3, 0x08, 0x06, 0x00, + 0x72, 3, 0x00, 0xff, 0x00, + 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, + 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, + 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, + 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, + 0xd2, 0xeb, + 0xaf, 1, 0x02, + 0xb5, 2, 0x08, 0x08, + 0xb8, 2, 0x08, 0x88, + 0xc4, 4, 0xae, 0x01, 0x04, 0x01, + 0xcc, 1, 0x00, + 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, + 0xc1, 0xd7, 0xec, + 0xdc, 1, 0x01, + 0xff, 1, 0x01, /* page 1 */ + 0x12, 3, 0x02, 0x00, 0x01, + 0x3e, 2, 0x00, 0x00, + 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, + 0x7c, 1, 0x00, + 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, + 0x02, 0x00, + 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, + 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, + 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, + 0xd8, 1, 0x01, + 0xdb, 2, 0x00, 0x01, + 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, + 0xe6, 4, 0x00, 0x00, 0x00, 0x01, + 0xeb, 1, 0x00, + 0xff, 1, 0x02, /* page 2 */ + 0x22, 1, 0x00, + 0xff, 1, 0x03, /* page 3 */ + 0, LOAD_PAGE3, /* load the page 3 */ + 0x11, 1, 0x01, + 0xff, 1, 0x02, /* page 2 */ + 0x13, 1, 0x00, + 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, + 0x27, 2, 0x14, 0x0c, + 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, + 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, + 0x6e, 1, 0x08, + 0xff, 1, 0x01, /* page 1 */ + 0x78, 1, 0x00, + 0, END_OF_SEQUENCE /* end of sequence */ +}; + +#define SKIP 0xaa +/* page 3 - the value SKIP says skip the index - see reg_w_page() */ +static const __u8 page3_7302[] = { + 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, + 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, + 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, + 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, + 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, + 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, + 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, + 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, + 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, + 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, + 0x00 +}; + +static int reg_w_buf(struct gspca_dev *gspca_dev, + __u8 index, + const char *buffer, int len) +{ + int ret; + + memcpy(gspca_dev->usb_buf, buffer, len); + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 1, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, /* value */ + index, gspca_dev->usb_buf, len, + 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w_buf(): " + "Failed to write registers to index 0x%x, error %i", + index, ret); + return ret; +} + + +static int reg_w(struct gspca_dev *gspca_dev, + __u8 index, + __u8 value) +{ + int ret; + + gspca_dev->usb_buf[0] = value; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w(): " + "Failed to write register to index 0x%x, value 0x%x, error %i", + index, value, ret); + return ret; +} + +static int reg_w_seq(struct gspca_dev *gspca_dev, + const __u8 *seq, int len) +{ + int ret = 0; + while (--len >= 0) { + if (0 <= ret) + ret = reg_w(gspca_dev, seq[0], seq[1]); + seq += 2; + } + return ret; +} + +/* load the beginning of a page */ +static int reg_w_page(struct gspca_dev *gspca_dev, + const __u8 *page, int len) +{ + int index; + int ret = 0; + + for (index = 0; index < len; index++) { + if (page[index] == SKIP) /* skip this index */ + continue; + gspca_dev->usb_buf[0] = page[index]; + ret = usb_control_msg(gspca_dev->dev, + usb_sndctrlpipe(gspca_dev->dev, 0), + 0, /* request */ + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, index, gspca_dev->usb_buf, 1, + 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w_page(): " + "Failed to write register to index 0x%x, " + "value 0x%x, error %i", + index, page[index], ret); + break; + } + } + return ret; +} + +/* output a variable sequence */ +static int reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq, + const __u8 *page3, unsigned int page3_len, + const __u8 *page4, unsigned int page4_len) +{ + int index, len; + int ret = 0; + + for (;;) { + index = *seq++; + len = *seq++; + switch (len) { + case END_OF_SEQUENCE: + return ret; + case LOAD_PAGE4: + ret = reg_w_page(gspca_dev, page4, page4_len); + break; + case LOAD_PAGE3: + ret = reg_w_page(gspca_dev, page3, page3_len); + break; + default: + if (len > USB_BUF_SZ) { + PDEBUG(D_ERR|D_STREAM, + "Incorrect variable sequence"); + return -EINVAL; + } + while (len > 0) { + if (len < 8) { + ret = reg_w_buf(gspca_dev, + index, seq, len); + if (ret < 0) + return ret; + seq += len; + break; + } + ret = reg_w_buf(gspca_dev, index, seq, 8); + seq += 8; + index += 8; + len -= 8; + } + } + if (ret < 0) + return ret; + } + /* not reached */ +} + +/* this function is called at probe time for pac7302 */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam; + + cam = &gspca_dev->cam; + + PDEBUG(D_CONF, "Find Sensor PAC7302"); + cam->cam_mode = vga_mode; /* only 640x480 */ + cam->nmodes = ARRAY_SIZE(vga_mode); + + sd->brightness = BRIGHTNESS_DEF; + sd->contrast = CONTRAST_DEF; + sd->colors = COLOR_DEF; + sd->white_balance = WHITEBALANCE_DEF; + sd->red_balance = REDBALANCE_DEF; + sd->blue_balance = BLUEBALANCE_DEF; + sd->gain = GAIN_DEF; + sd->exposure = EXPOSURE_DEF; + sd->autogain = AUTOGAIN_DEF; + sd->hflip = HFLIP_DEF; + sd->vflip = VFLIP_DEF; + return 0; +} + +/* This function is used by pac7302 only */ +static int setbrightcont(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + int ret; + static const __u8 max[10] = + {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, + 0xd4, 0xec}; + static const __u8 delta[10] = + {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, + 0x11, 0x0b}; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 10; i++) { + v = max[i]; + v += (sd->brightness - BRIGHTNESS_MAX) + * 150 / BRIGHTNESS_MAX; /* 200 ? */ + v -= delta[i] * sd->contrast / CONTRAST_MAX; + if (v < 0) + v = 0; + else if (v > 0xff) + v = 0xff; + if (0 <= ret) + ret = reg_w(gspca_dev, 0xa2 + i, v); + } + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + return ret; +} + +/* This function is used by pac7302 only */ +static int setcolors(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int i, v; + int ret; + static const int a[9] = + {217, -212, 0, -101, 170, -67, -38, -315, 355}; + static const int b[9] = + {19, 106, 0, 19, 106, 1, 19, 106, 1}; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + for (i = 0; i < 9; i++) { + v = a[i] * sd->colors / COLOR_MAX + b[i]; + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f + 2 * i + 1, v); + } + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); + return ret; +} + +static int setwhitebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc6, sd->white_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance); + return ret; +} + +static int setredbalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc5, sd->red_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance); + return ret; +} + +static int setbluebalance(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xc7, sd->blue_balance); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance); + return ret; +} + +static int setgain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x10, sd->gain >> 3); + + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +static int setexposure(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + __u8 reg; + + /* register 2 of frame 3/4 contains the clock divider configuring the + no fps according to the formula: 60 / reg. sd->exposure is the + desired exposure time in ms. */ + reg = 120 * sd->exposure / 1000; + if (reg < 2) + reg = 2; + else if (reg > 63) + reg = 63; + + /* On the pac7302 reg2 MUST be a multiple of 3, so round it to + the nearest multiple of 3, except when between 6 and 12? */ + if (reg < 6 || reg > 12) + reg = ((reg + 1) / 3) * 3; + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x02, reg); + + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +static int sethvflip(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret; + __u8 data; + + ret = reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ + data = (sd->hflip ? 0x08 : 0x00) | (sd->vflip ? 0x04 : 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x21, data); + /* load registers to sensor (Bit 0, auto clear) */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; +} + +/* this function is called at probe and resume time for pac7302 */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2); +} + +static int sd_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->sof_read = 0; + + ret = reg_w_var(gspca_dev, start_7302, + page3_7302, sizeof(page3_7302), + NULL, 0); + if (0 <= ret) + ret = setbrightcont(gspca_dev); + if (0 <= ret) + ret = setcolors(gspca_dev); + if (0 <= ret) + ret = setwhitebalance(gspca_dev); + if (0 <= ret) + ret = setredbalance(gspca_dev); + if (0 <= ret) + ret = setbluebalance(gspca_dev); + if (0 <= ret) + ret = setgain(gspca_dev); + if (0 <= ret) + ret = setexposure(gspca_dev); + if (0 <= ret) + ret = sethvflip(gspca_dev); + + /* only resolution 640x480 is supported for pac7302 */ + + sd->sof_read = 0; + sd->autogain_ignore_frames = 0; + atomic_set(&sd->avg_lum, -1); + + /* start stream */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x01); + + return ret; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + int ret; + + /* stop stream */ + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x00); +} + +/* called on streamoff with alt 0 and on disconnect for pac7302 */ +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + int ret; + + if (!gspca_dev->present) + return; + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x40); +} + +/* Include pac common sof detection functions */ +#include "pac_common.h" + +static void do_autogain(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int avg_lum = atomic_read(&sd->avg_lum); + int desired_lum, deadzone; + + if (avg_lum == -1) + return; + + desired_lum = 270 + sd->brightness * 4; + /* Hack hack, with the 7202 the first exposure step is + pretty large, so if we're about to make the first + exposure increase make the deadzone large to avoid + oscilating */ + if (desired_lum > avg_lum && sd->gain == GAIN_DEF && + sd->exposure > EXPOSURE_DEF && + sd->exposure < 42) + deadzone = 90; + else + deadzone = 30; + + if (sd->autogain_ignore_frames > 0) + sd->autogain_ignore_frames--; + else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum, + deadzone, GAIN_KNEE, EXPOSURE_KNEE)) + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; +} + +/* JPEG header, part 1 */ +static const unsigned char pac_jpeg_header1[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08 /* Precision: 8 */ + /* 2 bytes is placed here: number of image lines */ + /* 2 bytes is placed here: samples per line */ +}; + +/* JPEG header, continued */ +static const unsigned char pac_jpeg_header2[] = { + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ +}; + +static void pac_start_frame(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u16 lines, __u16 samples_per_line) +{ + unsigned char tmpbuf[4]; + + gspca_frame_add(gspca_dev, FIRST_PACKET, + pac_jpeg_header1, sizeof(pac_jpeg_header1)); + + tmpbuf[0] = lines >> 8; + tmpbuf[1] = lines & 0xff; + tmpbuf[2] = samples_per_line >> 8; + tmpbuf[3] = samples_per_line & 0xff; + + gspca_frame_add(gspca_dev, INTER_PACKET, + tmpbuf, sizeof(tmpbuf)); + gspca_frame_add(gspca_dev, INTER_PACKET, + pac_jpeg_header2, sizeof(pac_jpeg_header2)); +} + +/* this function is run at interrupt level */ +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + struct gspca_frame *frame; + unsigned char *sof; + + sof = pac_find_sof(&sd->sof_read, data, len); + if (sof) { + int n, lum_offset, footer_length; + + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* 6 bytes after the FF D9 EOF marker a number of lumination + bytes are send corresponding to different parts of the + image, the 14th and 15th byte after the EOF seem to + correspond to the center of the image */ + lum_offset = 61 + sizeof pac_sof_marker; + footer_length = 74; + + /* Finish decoding current frame */ + n = (sof - data) - (footer_length + sizeof pac_sof_marker); + if (n < 0) { + frame->data_end += n; + n = 0; + } + gspca_frame_add(gspca_dev, INTER_PACKET, + data, n); + if (gspca_dev->last_packet_type != DISCARD_PACKET && + frame->data_end[-2] == 0xff && + frame->data_end[-1] == 0xd9) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + + n = sof - data; + len -= n; + data = sof; + + /* Get average lumination */ + if (gspca_dev->last_packet_type == LAST_PACKET && + n >= lum_offset) + atomic_set(&sd->avg_lum, data[-lum_offset] + + data[-lum_offset + 1]); + else + atomic_set(&sd->avg_lum, -1); + + /* Start the new frame with the jpeg header */ + /* The PAC7302 has the image rotated 90 degrees */ + pac_start_frame(gspca_dev, frame, + gspca_dev->width, gspca_dev->height); + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} + +static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->brightness = val; + if (gspca_dev->streaming) + setbrightcont(gspca_dev); + return 0; +} + +static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->brightness; + return 0; +} + +static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->contrast = val; + if (gspca_dev->streaming) { + setbrightcont(gspca_dev); + } + return 0; +} + +static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->contrast; + return 0; +} + +static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->colors = val; + if (gspca_dev->streaming) + setcolors(gspca_dev); + return 0; +} + +static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->colors; + return 0; +} + +static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->white_balance = val; + if (gspca_dev->streaming) + ret = setwhitebalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->white_balance; + return 0; +} + +static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->red_balance = val; + if (gspca_dev->streaming) + ret = setredbalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->red_balance; + return 0; +} + +static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + int ret = 0; + + sd->blue_balance = val; + if (gspca_dev->streaming) + ret = setbluebalance(gspca_dev); + if (0 <= ret) + ret = 0; + return ret; +} + +static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->blue_balance; + return 0; +} + +static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->gain = val; + if (gspca_dev->streaming) + setgain(gspca_dev); + return 0; +} + +static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->gain; + return 0; +} + +static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->exposure = val; + if (gspca_dev->streaming) + setexposure(gspca_dev); + return 0; +} + +static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->exposure; + return 0; +} + +static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->autogain = val; + /* when switching to autogain set defaults to make sure + we are on a valid point of the autogain gain / + exposure knee graph, and give this change time to + take effect before doing autogain. */ + if (sd->autogain) { + sd->exposure = EXPOSURE_DEF; + sd->gain = GAIN_DEF; + if (gspca_dev->streaming) { + sd->autogain_ignore_frames = + PAC_AUTOGAIN_IGNORE_FRAMES; + setexposure(gspca_dev); + setgain(gspca_dev); + } + } + + return 0; +} + +static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->autogain; + return 0; +} + +static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->hflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->hflip; + return 0; +} + +static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + sd->vflip = val; + if (gspca_dev->streaming) + sethvflip(gspca_dev); + return 0; +} + +static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) +{ + struct sd *sd = (struct sd *) gspca_dev; + + *val = sd->vflip; + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int sd_dbg_s_register(struct gspca_dev *gspca_dev, + struct v4l2_dbg_register *reg) +{ + int ret = -EINVAL; + __u8 index; + __u8 value; + + /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit + long on the USB bus) + */ + if (reg->match.type == V4L2_CHIP_MATCH_HOST && + reg->match.addr == 0 && + (reg->reg < 0x000000ff) && + (reg->val <= 0x000000ff) + ) { + /* Currently writing to page 0 is only supported. */ + /* reg_w() only supports 8bit index */ + index = reg->reg & 0x000000ff; + value = reg->val & 0x000000ff; + + /* Note that there shall be no access to other page + by any other function between the page swith and + the actual register write */ + ret = reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ + if (0 <= ret) + ret = reg_w(gspca_dev, index, value); + + if (0 <= ret) + ret = reg_w(gspca_dev, 0xdc, 0x01); + } + return ret; +} + +static int sd_chip_ident(struct gspca_dev *gspca_dev, + struct v4l2_dbg_chip_ident *chip) +{ + int ret = -EINVAL; + + if (chip->match.type == V4L2_CHIP_MATCH_HOST && + chip->match.addr == 0) { + chip->revision = 0; + chip->ident = V4L2_IDENT_UNKNOWN; + ret = 0; + } + return ret; +} +#endif + +/* sub-driver description for pac7302 */ +static struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, + .dq_callback = do_autogain, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .set_register = sd_dbg_s_register, + .get_chip_ident = sd_chip_ident, +#endif +}; + +/* -- module initialisation -- */ +static __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x06f8, 0x3009)}, + {USB_DEVICE(0x093a, 0x2620)}, + {USB_DEVICE(0x093a, 0x2621)}, + {USB_DEVICE(0x093a, 0x2622)}, + {USB_DEVICE(0x093a, 0x2624)}, + {USB_DEVICE(0x093a, 0x2626)}, + {USB_DEVICE(0x093a, 0x2628)}, + {USB_DEVICE(0x093a, 0x2629)}, + {USB_DEVICE(0x093a, 0x262a)}, + {USB_DEVICE(0x093a, 0x262c)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 052714484e83b..e5697a6345e8f 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -57,23 +57,17 @@ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); -/* specific webcam descriptor */ +/* specific webcam descriptor for pac7311 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char brightness; unsigned char contrast; - unsigned char colors; unsigned char gain; unsigned char exposure; unsigned char autogain; __u8 hflip; __u8 vflip; - __u8 sensor; -#define SENSOR_PAC7302 0 -#define SENSOR_PAC7311 1 - u8 sof_read; u8 autogain_ignore_frames; @@ -81,12 +75,8 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val); static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val); -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val); static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val); static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val); @@ -99,23 +89,6 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val); static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val); static struct ctrl sd_ctrls[] = { -/* This control is pac7302 only */ -#define BRIGHTNESS_IDX 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, -#define BRIGHTNESS_MAX 0x20 - .maximum = BRIGHTNESS_MAX, - .step = 1, -#define BRIGHTNESS_DEF 0x10 - .default_value = BRIGHTNESS_DEF, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, /* This control is for both the 7302 and the 7311 */ { { @@ -132,23 +105,6 @@ static struct ctrl sd_ctrls[] = { .set = sd_setcontrast, .get = sd_getcontrast, }, -/* This control is pac7302 only */ -#define SATURATION_IDX 2 - { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, -#define COLOR_MAX 255 - .maximum = COLOR_MAX, - .step = 1, -#define COLOR_DEF 127 - .default_value = COLOR_DEF, - }, - .set = sd_setcolors, - .get = sd_getcolors, - }, /* All controls below are for both the 7302 and the 7311 */ { { @@ -244,101 +200,9 @@ static const struct v4l2_pix_format vga_mode[] = { .priv = 0}, }; -/* pac 7302 */ -static const __u8 init_7302[] = { -/* index,value */ - 0xff, 0x01, /* page 1 */ - 0x78, 0x00, /* deactivate */ - 0xff, 0x01, - 0x78, 0x40, /* led off */ -}; -static const __u8 start_7302[] = { -/* index, len, [value]* */ - 0xff, 1, 0x00, /* page 0 */ - 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80, - 0x00, 0x00, 0x00, 0x00, - 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00, - 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7, - 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11, - 0x26, 2, 0xaa, 0xaa, - 0x2e, 1, 0x31, - 0x38, 1, 0x01, - 0x3a, 3, 0x14, 0xff, 0x5a, - 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11, - 0x00, 0x54, 0x11, - 0x55, 1, 0x00, - 0x62, 4, 0x10, 0x1e, 0x1e, 0x18, - 0x6b, 1, 0x00, - 0x6e, 3, 0x08, 0x06, 0x00, - 0x72, 3, 0x00, 0xff, 0x00, - 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c, - 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50, - 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00, - 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9, - 0xd2, 0xeb, - 0xaf, 1, 0x02, - 0xb5, 2, 0x08, 0x08, - 0xb8, 2, 0x08, 0x88, - 0xc4, 4, 0xae, 0x01, 0x04, 0x01, - 0xcc, 1, 0x00, - 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9, - 0xc1, 0xd7, 0xec, - 0xdc, 1, 0x01, - 0xff, 1, 0x01, /* page 1 */ - 0x12, 3, 0x02, 0x00, 0x01, - 0x3e, 2, 0x00, 0x00, - 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2, - 0x7c, 1, 0x00, - 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20, - 0x02, 0x00, - 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04, - 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, - 0x07, 0x00, 0x01, 0x07, 0x04, 0x01, - 0xd8, 1, 0x01, - 0xdb, 2, 0x00, 0x01, - 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00, - 0xe6, 4, 0x00, 0x00, 0x00, 0x01, - 0xeb, 1, 0x00, - 0xff, 1, 0x02, /* page 2 */ - 0x22, 1, 0x00, - 0xff, 1, 0x03, /* page 3 */ - 0x00, 255, /* load the page 3 */ - 0x11, 1, 0x01, - 0xff, 1, 0x02, /* page 2 */ - 0x13, 1, 0x00, - 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96, - 0x27, 2, 0x14, 0x0c, - 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22, - 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44, - 0x6e, 1, 0x08, - 0xff, 1, 0x01, /* page 1 */ - 0x78, 1, 0x00, - 0, 0 /* end of sequence */ -}; - -/* page 3 - the value 0xaa says skip the index - see reg_w_page() */ -static const __u8 page3_7302[] = { - 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16, - 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00, - 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00, - 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21, - 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54, - 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00, - 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00, - 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00, - 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8, - 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, - 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00, - 0x00 -}; +#define LOAD_PAGE3 255 +#define LOAD_PAGE4 254 +#define END_OF_SEQUENCE 0 /* pac 7311 */ static const __u8 init_7311[] = { @@ -378,119 +242,154 @@ static const __u8 start_7311[] = { 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00, 0x3f, 0x00, 0x0a, 0x01, 0x00, 0xff, 1, 0x04, /* page 4 */ - 0x00, 254, /* load the page 4 */ + 0, LOAD_PAGE4, /* load the page 4 */ 0x11, 1, 0x01, - 0, 0 /* end of sequence */ + 0, END_OF_SEQUENCE /* end of sequence */ }; -/* page 4 - the value 0xaa says skip the index - see reg_w_page() */ +#define SKIP 0xaa +/* page 4 - the value SKIP says skip the index - see reg_w_page() */ static const __u8 page4_7311[] = { - 0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, - 0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62, - 0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa, - 0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68, + SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f, + 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62, + 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP, + SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68, 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00, 0x23, 0x28, 0x04, 0x11, 0x00, 0x00 }; -static void reg_w_buf(struct gspca_dev *gspca_dev, +static int reg_w_buf(struct gspca_dev *gspca_dev, __u8 index, const char *buffer, int len) { + int ret; + memcpy(gspca_dev->usb_buf, buffer, len); - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 1, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, /* value */ index, gspca_dev->usb_buf, len, 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w_buf(): " + "Failed to write registers to index 0x%x, error %i", + index, ret); + return ret; } -static void reg_w(struct gspca_dev *gspca_dev, +static int reg_w(struct gspca_dev *gspca_dev, __u8 index, __u8 value) { + int ret; + gspca_dev->usb_buf[0] = value; - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) + PDEBUG(D_ERR, "reg_w(): " + "Failed to write register to index 0x%x, value 0x%x, error %i", + index, value, ret); + return ret; } -static void reg_w_seq(struct gspca_dev *gspca_dev, +static int reg_w_seq(struct gspca_dev *gspca_dev, const __u8 *seq, int len) { + int ret = 0; while (--len >= 0) { - reg_w(gspca_dev, seq[0], seq[1]); + if (0 <= ret) + ret = reg_w(gspca_dev, seq[0], seq[1]); seq += 2; } + return ret; } /* load the beginning of a page */ -static void reg_w_page(struct gspca_dev *gspca_dev, +static int reg_w_page(struct gspca_dev *gspca_dev, const __u8 *page, int len) { int index; + int ret = 0; for (index = 0; index < len; index++) { - if (page[index] == 0xaa) /* skip this index */ + if (page[index] == SKIP) /* skip this index */ continue; gspca_dev->usb_buf[0] = page[index]; - usb_control_msg(gspca_dev->dev, + ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 0, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, 0, index, gspca_dev->usb_buf, 1, 500); + if (ret < 0) { + PDEBUG(D_ERR, "reg_w_page(): " + "Failed to write register to index 0x%x, " + "value 0x%x, error %i", + index, page[index], ret); + break; + } } + return ret; } /* output a variable sequence */ -static void reg_w_var(struct gspca_dev *gspca_dev, - const __u8 *seq) +static int reg_w_var(struct gspca_dev *gspca_dev, + const __u8 *seq, + const __u8 *page3, unsigned int page3_len, + const __u8 *page4, unsigned int page4_len) { int index, len; + int ret = 0; for (;;) { index = *seq++; len = *seq++; switch (len) { - case 0: - return; - case 254: - reg_w_page(gspca_dev, page4_7311, sizeof page4_7311); + case END_OF_SEQUENCE: + return ret; + case LOAD_PAGE4: + ret = reg_w_page(gspca_dev, page4, page4_len); break; - case 255: - reg_w_page(gspca_dev, page3_7302, sizeof page3_7302); + case LOAD_PAGE3: + ret = reg_w_page(gspca_dev, page3, page3_len); break; default: - if (len > 64) { + if (len > USB_BUF_SZ) { PDEBUG(D_ERR|D_STREAM, "Incorrect variable sequence"); - return; + return -EINVAL; } while (len > 0) { if (len < 8) { - reg_w_buf(gspca_dev, index, seq, len); + ret = reg_w_buf(gspca_dev, + index, seq, len); + if (ret < 0) + return ret; seq += len; break; } - reg_w_buf(gspca_dev, index, seq, 8); + ret = reg_w_buf(gspca_dev, index, seq, 8); seq += 8; index += 8; len -= 8; } } + if (ret < 0) + return ret; } /* not reached */ } -/* this function is called at probe time */ +/* this function is called at probe time for pac7311 */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { @@ -499,22 +398,11 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; - sd->sensor = id->driver_info; - if (sd->sensor == SENSOR_PAC7302) { - PDEBUG(D_CONF, "Find Sensor PAC7302"); - cam->cam_mode = &vga_mode[2]; /* only 640x480 */ - cam->nmodes = 1; - } else { - PDEBUG(D_CONF, "Find Sensor PAC7311"); - cam->cam_mode = vga_mode; - cam->nmodes = ARRAY_SIZE(vga_mode); - gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) - | (1 << SATURATION_IDX); - } + PDEBUG(D_CONF, "Find Sensor PAC7311"); + cam->cam_mode = vga_mode; + cam->nmodes = ARRAY_SIZE(vga_mode); - sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; - sd->colors = COLOR_DEF; sd->gain = GAIN_DEF; sd->exposure = EXPOSURE_DEF; sd->autogain = AUTOGAIN_DEF; @@ -523,91 +411,47 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* This function is used by pac7302 only */ -static void setbrightcont(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i, v; - static const __u8 max[10] = - {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb, - 0xd4, 0xec}; - static const __u8 delta[10] = - {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17, - 0x11, 0x0b}; - - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - for (i = 0; i < 10; i++) { - v = max[i]; - v += (sd->brightness - BRIGHTNESS_MAX) - * 150 / BRIGHTNESS_MAX; /* 200 ? */ - v -= delta[i] * sd->contrast / CONTRAST_MAX; - if (v < 0) - v = 0; - else if (v > 0xff) - v = 0xff; - reg_w(gspca_dev, 0xa2 + i, v); - } - reg_w(gspca_dev, 0xdc, 0x01); -} - /* This function is used by pac7311 only */ -static void setcontrast(struct gspca_dev *gspca_dev) +static int setcontrast(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, sd->contrast >> 4); + ret = reg_w(gspca_dev, 0xff, 0x04); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x10, sd->contrast >> 4); /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -/* This function is used by pac7302 only */ -static void setcolors(struct gspca_dev *gspca_dev) +static int setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - int i, v; - static const int a[9] = - {217, -212, 0, -101, 170, -67, -38, -315, 355}; - static const int b[9] = - {19, 106, 0, 19, 106, 1, 19, 106, 1}; - - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x11, 0x01); - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ - for (i = 0; i < 9; i++) { - v = a[i] * sd->colors / COLOR_MAX + b[i]; - reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07); - reg_w(gspca_dev, 0x0f + 2 * i + 1, v); - } - reg_w(gspca_dev, 0xdc, 0x01); - PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors); -} + int gain = GAIN_MAX - sd->gain; + int ret; -static void setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; + if (gain < 1) + gain = 1; + else if (gain > 245) + gain = 245; + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0e, 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x0f, gain); - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, sd->gain >> 3); - } else { - int gain = GAIN_MAX - sd->gain; - if (gain < 1) - gain = 1; - else if (gain > 245) - gain = 245; - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x0e, 0x00); - reg_w(gspca_dev, 0x0f, gain); - } /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -static void setexposure(struct gspca_dev *gspca_dev) +static int setexposure(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; __u8 reg; /* register 2 of frame 3/4 contains the clock divider configuring the @@ -619,97 +463,94 @@ static void setexposure(struct gspca_dev *gspca_dev) else if (reg > 63) reg = 63; - if (sd->sensor == SENSOR_PAC7302) { - /* On the pac7302 reg2 MUST be a multiple of 3, so round it to - the nearest multiple of 3, except when between 6 and 12? */ - if (reg < 6 || reg > 12) - reg = ((reg + 1) / 3) * 3; - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x02, reg); + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x02, reg); + /* Page 1 register 8 must always be 0x08 except when not in + 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && + reg <= 3) { + if (0 <= ret) + ret = reg_w(gspca_dev, 0x08, 0x09); } else { - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, reg); - /* Page 1 register 8 must always be 0x08 except when not in - 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */ - reg_w(gspca_dev, 0xff, 0x01); - if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv && - reg <= 3) - reg_w(gspca_dev, 0x08, 0x09); - else - reg_w(gspca_dev, 0x08, 0x08); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x08, 0x08); } + /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -static void sethvflip(struct gspca_dev *gspca_dev) +static int sethvflip(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; __u8 data; - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - data = (sd->hflip ? 0x08 : 0x00) - | (sd->vflip ? 0x04 : 0x00); - } else { - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - data = (sd->hflip ? 0x04 : 0x00) - | (sd->vflip ? 0x08 : 0x00); - } - reg_w(gspca_dev, 0x21, data); + ret = reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ + data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x21, data); /* load registers to sensor (Bit 0, auto clear) */ - reg_w(gspca_dev, 0x11, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x11, 0x01); + return ret; } -/* this function is called at probe and resume time */ +/* this function is called at probe and resume time for pac7311 */ static int sd_init(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAC7302) - reg_w_seq(gspca_dev, init_7302, sizeof init_7302); - else - reg_w_seq(gspca_dev, init_7311, sizeof init_7311); - - return 0; + return reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2); } static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + int ret; sd->sof_read = 0; - if (sd->sensor == SENSOR_PAC7302) { - reg_w_var(gspca_dev, start_7302); - setbrightcont(gspca_dev); - setcolors(gspca_dev); - } else { - reg_w_var(gspca_dev, start_7311); - setcontrast(gspca_dev); - } - setgain(gspca_dev); - setexposure(gspca_dev); - sethvflip(gspca_dev); + ret = reg_w_var(gspca_dev, start_7311, + NULL, 0, + page4_7311, sizeof(page4_7311)); + if (0 <= ret) + ret = setcontrast(gspca_dev); + if (0 <= ret) + ret = setgain(gspca_dev); + if (0 <= ret) + ret = setexposure(gspca_dev); + if (0 <= ret) + ret = sethvflip(gspca_dev); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { case 2: /* 160x120 pac7311 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x20); - reg_w(gspca_dev, 0x87, 0x10); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x20); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x10); break; case 1: /* 320x240 pac7311 */ - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x30); - reg_w(gspca_dev, 0x87, 0x11); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x30); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x11); break; case 0: /* 640x480 */ - if (sd->sensor == SENSOR_PAC7302) - break; - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x17, 0x00); - reg_w(gspca_dev, 0x87, 0x12); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x17, 0x00); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x87, 0x12); break; } @@ -718,47 +559,42 @@ static int sd_start(struct gspca_dev *gspca_dev) atomic_set(&sd->avg_lum, -1); /* start stream */ - reg_w(gspca_dev, 0xff, 0x01); - if (sd->sensor == SENSOR_PAC7302) - reg_w(gspca_dev, 0x78, 0x01); - else - reg_w(gspca_dev, 0x78, 0x05); - return 0; + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x05); + + return ret; } static void sd_stopN(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x00); - reg_w(gspca_dev, 0x78, 0x00); - return; - } - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x27, 0x80); - reg_w(gspca_dev, 0x28, 0xca); - reg_w(gspca_dev, 0x29, 0x53); - reg_w(gspca_dev, 0x2a, 0x0e); - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x3e, 0x20); - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ - reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ -} + int ret; -/* called on streamoff with alt 0 and on disconnect */ + ret = reg_w(gspca_dev, 0xff, 0x04); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x27, 0x80); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x28, 0xca); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x29, 0x53); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x2a, 0x0e); + if (0 <= ret) + ret = reg_w(gspca_dev, 0xff, 0x01); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x3e, 0x20); + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ + if (0 <= ret) + ret = reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */ +} + +/* called on streamoff with alt 0 and on disconnect for 7311 */ static void sd_stop0(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (!gspca_dev->present) - return; - if (sd->sensor == SENSOR_PAC7302) { - reg_w(gspca_dev, 0xff, 0x01); - reg_w(gspca_dev, 0x78, 0x40); - } } /* Include pac common sof detection functions */ @@ -773,22 +609,8 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (avg_lum == -1) return; - if (sd->sensor == SENSOR_PAC7302) { - desired_lum = 270 + sd->brightness * 4; - /* Hack hack, with the 7202 the first exposure step is - pretty large, so if we're about to make the first - exposure increase make the deadzone large to avoid - oscilating */ - if (desired_lum > avg_lum && sd->gain == GAIN_DEF && - sd->exposure > EXPOSURE_DEF && - sd->exposure < 42) - deadzone = 90; - else - deadzone = 30; - } else { - desired_lum = 200; - deadzone = 20; - } + desired_lum = 200; + deadzone = 20; if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; @@ -797,53 +619,92 @@ static void do_autogain(struct gspca_dev *gspca_dev) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } -static const unsigned char pac7311_jpeg_header1[] = { - 0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08 +/* JPEG header, part 1 */ +static const unsigned char pac_jpeg_header1[] = { + 0xff, 0xd8, /* SOI: Start of Image */ + + 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */ + 0x00, 0x11, /* length = 17 bytes (including this length field) */ + 0x08 /* Precision: 8 */ + /* 2 bytes is placed here: number of image lines */ + /* 2 bytes is placed here: samples per line */ }; -static const unsigned char pac7311_jpeg_header2[] = { - 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda, - 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +/* JPEG header, continued */ +static const unsigned char pac_jpeg_header2[] = { + 0x03, /* Number of image components: 3 */ + 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */ + 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */ + 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */ + + 0xff, 0xda, /* SOS: Start Of Scan */ + 0x00, 0x0c, /* length = 12 bytes (including this length field) */ + 0x03, /* number of components: 3 */ + 0x01, 0x00, /* selector 1, table 0x00 */ + 0x02, 0x11, /* selector 2, table 0x11 */ + 0x03, 0x11, /* selector 3, table 0x11 */ + 0x00, 0x3f, /* Spectral selection: 0 .. 63 */ + 0x00 /* Successive approximation: 0 */ }; +static void pac_start_frame(struct gspca_dev *gspca_dev, + struct gspca_frame *frame, + __u16 lines, __u16 samples_per_line) +{ + unsigned char tmpbuf[4]; + + gspca_frame_add(gspca_dev, FIRST_PACKET, + pac_jpeg_header1, sizeof(pac_jpeg_header1)); + + tmpbuf[0] = lines >> 8; + tmpbuf[1] = lines & 0xff; + tmpbuf[2] = samples_per_line >> 8; + tmpbuf[3] = samples_per_line & 0xff; + + gspca_frame_add(gspca_dev, INTER_PACKET, + tmpbuf, sizeof(tmpbuf)); + gspca_frame_add(gspca_dev, INTER_PACKET, + pac_jpeg_header2, sizeof(pac_jpeg_header2)); +} + /* this function is run at interrupt level */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; unsigned char *sof; + struct gspca_frame *frame; - sof = pac_find_sof(gspca_dev, data, len); + sof = pac_find_sof(&sd->sof_read, data, len); if (sof) { - unsigned char tmpbuf[4]; int n, lum_offset, footer_length; - if (sd->sensor == SENSOR_PAC7302) { - /* 6 bytes after the FF D9 EOF marker a number of lumination - bytes are send corresponding to different parts of the - image, the 14th and 15th byte after the EOF seem to - correspond to the center of the image */ - lum_offset = 61 + sizeof pac_sof_marker; - footer_length = 74; - } else { - lum_offset = 24 + sizeof pac_sof_marker; - footer_length = 26; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; } + /* 6 bytes after the FF D9 EOF marker a number of lumination + bytes are send corresponding to different parts of the + image, the 14th and 15th byte after the EOF seem to + correspond to the center of the image */ + lum_offset = 24 + sizeof pac_sof_marker; + footer_length = 26; + /* Finish decoding current frame */ n = (sof - data) - (footer_length + sizeof pac_sof_marker); if (n < 0) { frame->data_end += n; n = 0; } - frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, n); if (gspca_dev->last_packet_type != DISCARD_PACKET && frame->data_end[-2] == 0xff && frame->data_end[-1] == 0xd9) - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); n = sof - data; @@ -859,43 +720,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, atomic_set(&sd->avg_lum, -1); /* Start the new frame with the jpeg header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1)); - if (sd->sensor == SENSOR_PAC7302) { - /* The PAC7302 has the image rotated 90 degrees */ - tmpbuf[0] = gspca_dev->width >> 8; - tmpbuf[1] = gspca_dev->width & 0xff; - tmpbuf[2] = gspca_dev->height >> 8; - tmpbuf[3] = gspca_dev->height & 0xff; - } else { - tmpbuf[0] = gspca_dev->height >> 8; - tmpbuf[1] = gspca_dev->height & 0xff; - tmpbuf[2] = gspca_dev->width >> 8; - tmpbuf[3] = gspca_dev->width & 0xff; - } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2)); + pac_start_frame(gspca_dev, frame, + gspca_dev->height, gspca_dev->width); } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); -} - -static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->brightness = val; - if (gspca_dev->streaming) - setbrightcont(gspca_dev); - return 0; -} - -static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->brightness; - return 0; + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) @@ -904,10 +732,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val) sd->contrast = val; if (gspca_dev->streaming) { - if (sd->sensor == SENSOR_PAC7302) - setbrightcont(gspca_dev); - else - setcontrast(gspca_dev); + setcontrast(gspca_dev); } return 0; } @@ -920,24 +745,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->colors = val; - if (gspca_dev->streaming) - setcolors(gspca_dev); - return 0; -} - -static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->colors; - return 0; -} - static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -1041,7 +848,7 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) return 0; } -/* sub-driver description */ +/* sub-driver description for pac7311 */ static struct sd_desc sd_desc = { .name = MODULE_NAME, .ctrls = sd_ctrls, @@ -1057,21 +864,12 @@ static struct sd_desc sd_desc = { /* -- module initialisation -- */ static __devinitdata struct usb_device_id device_table[] = { - {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311}, - {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302}, - {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302}, + {USB_DEVICE(0x093a, 0x2600)}, + {USB_DEVICE(0x093a, 0x2601)}, + {USB_DEVICE(0x093a, 0x2603)}, + {USB_DEVICE(0x093a, 0x2608)}, + {USB_DEVICE(0x093a, 0x260e)}, + {USB_DEVICE(0x093a, 0x260f)}, {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h index 34d4b1494cd5b..20f67d9b8c06e 100644 --- a/drivers/media/video/gspca/pac_common.h +++ b/drivers/media/video/gspca/pac_common.h @@ -33,26 +33,101 @@ static const unsigned char pac_sof_marker[5] = { 0xff, 0xff, 0x00, 0xff, 0x96 }; -static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev, +/* + The following state machine finds the SOF marker sequence + 0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream. + + +----------+ + | 0: START |<---------------\ + +----------+<-\ | + | \---/otherwise | + v 0xff | + +----------+ otherwise | + | 1 |--------------->* + | | ^ + +----------+ | + | | + v 0xff | + +----------+<-\0xff | + /->| |--/ | + | | 2 |--------------->* + | | | otherwise ^ + | +----------+ | + | | | + | v 0x00 | + | +----------+ | + | | 3 | | + | | |--------------->* + | +----------+ otherwise ^ + | | | + 0xff | v 0xff | + | +----------+ | + \--| 4 | | + | |----------------/ + +----------+ otherwise + | + v 0x96 + +----------+ + | FOUND | + +----------+ +*/ + +static unsigned char *pac_find_sof(u8 *sof_read, unsigned char *m, int len) { - struct sd *sd = (struct sd *) gspca_dev; int i; /* Search for the SOF marker (fixed part) in the header */ for (i = 0; i < len; i++) { - if (m[i] == pac_sof_marker[sd->sof_read]) { - sd->sof_read++; - if (sd->sof_read == sizeof(pac_sof_marker)) { + switch (*sof_read) { + case 0: + if (m[i] == 0xff) + *sof_read = 1; + break; + case 1: + if (m[i] == 0xff) + *sof_read = 2; + else + *sof_read = 0; + break; + case 2: + switch (m[i]) { + case 0x00: + *sof_read = 3; + break; + case 0xff: + /* stay in this state */ + break; + default: + *sof_read = 0; + } + break; + case 3: + if (m[i] == 0xff) + *sof_read = 4; + else + *sof_read = 0; + break; + case 4: + switch (m[i]) { + case 0x96: + /* Pattern found */ PDEBUG(D_FRAM, "SOF found, bytes to analyze: %u." " Frame starts at byte #%u", len, i + 1); - sd->sof_read = 0; + *sof_read = 0; return m + i + 1; + break; + case 0xff: + *sof_read = 2; + break; + default: + *sof_read = 0; } - } else { - sd->sof_read = 0; + break; + default: + *sof_read = 0; } } diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index cdad3db333679..b1944a7cbb0f3 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -2342,7 +2342,6 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -2378,22 +2377,22 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, avg_lum >>= 9; atomic_set(&sd->avg_lum, avg_lum); gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, len); + data, len); return; } if (gspca_dev->last_packet_type == LAST_PACKET) { if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & MODE_JPEG) { - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } else { - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } } else { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } } diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index e39efb45fa1c1..5be95bc65138b 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -995,8 +995,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - unsigned char *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { int i; @@ -1054,12 +1053,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, pkt_type = DISCARD_PACKET; } - frame = gspca_frame_add(gspca_dev, pkt_type, - frame, data, 0); + gspca_frame_add(gspca_dev, pkt_type, + NULL, 0); data += i + fr_h_sz; len -= i + fr_h_sz; gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + data, len); return; } } @@ -1068,15 +1067,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) { /* In raw mode we sometimes get some garbage after the frame ignore this */ - int used = frame->data_end - frame->data; + struct gspca_frame *frame; + int used; int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage; + frame = gspca_get_i_frame(gspca_dev); + if (frame == NULL) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + used = frame->data_end - frame->data; if (used + len > size) len = size - used; } - gspca_frame_add(gspca_dev, INTER_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 33f4d0a1f6fd5..0bd36a00dd2a0 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -1,8 +1,8 @@ /* - * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library - * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr + * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver * - * V4L2 by Jean-Francois Moine + * Copyright (C) 2009 Jean-Francois Moine + * Copyright (C) 2005 Michel Xhaard mxhaard@magic.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 @@ -72,8 +72,9 @@ struct sd { #define SENSOR_OV7630 5 #define SENSOR_OV7648 6 #define SENSOR_OV7660 7 -#define SENSOR_SP80708 8 - u8 i2c_base; +#define SENSOR_PO1030 8 +#define SENSOR_SP80708 9 + u8 i2c_addr; u8 *jpeg_hdr; }; @@ -250,7 +251,7 @@ static struct ctrl sd_ctrls[] = { .minimum = 0, .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */ .step = 1, -#define FREQ_DEF 2 +#define FREQ_DEF 1 .default_value = FREQ_DEF, }, .set = sd_setfreq, @@ -277,7 +278,9 @@ static __u32 ctrl_dis[] = { (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX), /* SENSOR_OV7660 7 */ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | - (1 << FREQ_IDX), /* SENSOR_SP80708 8 */ + (1 << FREQ_IDX), /* SENSOR_PO1030 8 */ + (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | + (1 << FREQ_IDX), /* SENSOR_SP80708 9 */ }; static const struct v4l2_pix_format vga_mode[] = { @@ -304,7 +307,7 @@ static const u8 sn_hv7131[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41, /* reg18 reg19 reg1a reg1b */ @@ -315,7 +318,7 @@ static const u8 sn_mi0360[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61, /* reg18 reg19 reg1a reg1b */ @@ -337,7 +340,7 @@ static const u8 sn_mt9v111[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x5c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40, /* reg18 reg19 reg1a reg1b */ @@ -346,7 +349,7 @@ static const u8 sn_mt9v111[0x1c] = { static const u8 sn_om6802[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ - 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20, + 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19, /* reg8 reg9 rega regb regc regd rege regf */ 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ @@ -359,7 +362,7 @@ static const u8 sn_ov7630[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2, /* reg18 reg19 reg1a reg1b */ @@ -370,7 +373,7 @@ static const u8 sn_ov7648[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, + 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00, /* reg18 reg19 reg1a reg1b */ @@ -388,11 +391,22 @@ static const u8 sn_ov7660[0x1c] = { 0x07, 0x00, 0x00, 0x00 }; +static const u8 sn_po1030[0x1c] = { +/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ + 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20, +/* reg8 reg9 rega regb regc regd rege regf */ + 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ + 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00, +/* reg18 reg19 reg1a reg1b */ + 0x07, 0x00, 0x00, 0x00 +}; + static const u8 sn_sp80708[0x1c] = { /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20, /* reg8 reg9 rega regb regc regd rege regf */ - 0x81, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */ 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00, /* reg18 reg19 reg1a reg1b */ @@ -409,6 +423,7 @@ static const u8 *sn_tb[] = { sn_ov7630, sn_ov7648, sn_ov7660, + sn_po1030, sn_sp80708 }; @@ -455,7 +470,7 @@ static const u8 hv7131r_sensor_init[][8] = { {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - {0xa1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10}, @@ -464,6 +479,8 @@ static const u8 hv7131r_sensor_init[][8] = { {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10}, + /* set sensor clock */ {} }; static const u8 mi0360_sensor_init[][8] = { @@ -545,7 +562,7 @@ static const u8 mo4000_sensor_init[][8] = { }; static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */ - /* delay 20 ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */ {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */ @@ -572,7 +589,9 @@ static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, - /*******/ + {} +}; +static const u8 mt9v111_sensor_param1[][8] = { {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10}, @@ -585,14 +604,20 @@ static const u8 mt9v111_sensor_init[][8] = { {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */ {} }; +static const u8 om6802_init0[2][8] = { +/*fixme: variable*/ + {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10}, +}; static const u8 om6802_sensor_init[][8] = { - {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10}, - {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10}, + /* factory mode */ {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10}, + /* output raw RGB */ + {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10}, /* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */ {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10}, - /* white balance & auto-exposure */ + /* auto-exposure speed (0) / white balance mode (auto RGB) */ /* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10}, * set color mode */ /* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10}, @@ -606,26 +631,29 @@ static const u8 om6802_sensor_init[][8] = { /* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10}, * preset gamma */ {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10}, - /* luminance mode (0x4f = AE) */ + /* luminance mode (0x4f -> AutoExpo on) */ {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10}, /* preset shutter */ /* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10}, * auto frame rate */ /* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */ - -/* {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, */ -/* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */ + {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10}, + {} +}; +static const u8 om6802_sensor_param1[][8] = { + {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, + {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, {} }; static const u8 ov7630_sensor_init[][8] = { {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, -/* win: delay 20ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10}, -/* win: delay 20ms */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10}, /* win: i2c_r from 00 to 80 */ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10}, @@ -677,6 +705,7 @@ static const u8 ov7630_sensor_init[][8] = { static const u8 ov7648_sensor_init[][8] = { {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10}, {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10}, {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10}, @@ -701,7 +730,9 @@ static const u8 ov7648_sensor_init[][8] = { /* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */ /* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */ /* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */ -/*...*/ + {} +}; +static const u8 ov7648_sensor_param1[][8] = { /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */ /* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN * set by setvflip */ @@ -723,7 +754,7 @@ static const u8 ov7648_sensor_init[][8] = { static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */ -/* (delay 20ms) */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10}, /* Outformat = rawRGB */ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */ @@ -783,8 +814,11 @@ static const u8 ov7660_sensor_init[][8] = { {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */ {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */ {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */ +/* not in all ms-win traces*/ {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10}, -/****** (some exchanges in the win trace) ******/ + {} +}; +static const u8 ov7660_sensor_param1[][8] = { {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */ /* bits[3..0]reserved */ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, @@ -797,6 +831,7 @@ static const u8 ov7660_sensor_init[][8] = { {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */ /* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */ /****** (some exchanges in the win trace) ******/ +/*fixme:param2*/ {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */ {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */ @@ -804,6 +839,7 @@ static const u8 ov7660_sensor_init[][8] = { /* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */ /****** (some exchanges in the win trace) ******/ /******!! startsensor KO if changed !!****/ +/*fixme: param3*/ {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, @@ -811,6 +847,60 @@ static const u8 ov7660_sensor_init[][8] = { {} }; +static const u8 po1030_sensor_init[][8] = { +/* the sensor registers are described in m5602/m5602_po1030.h */ + {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */ + {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */ + {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10}, + {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10}, + {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10}, + {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */ + {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10}, + {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10}, + {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */ + {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10}, + {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */ + {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10}, + {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10}, + {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10}, + {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10}, + {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10}, + {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10}, + {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */ + {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10}, + + {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10}, + {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10}, + {} +}; +static const u8 po1030_sensor_param1[][8] = { +/* from ms-win traces - these values change with auto gain/expo/wb.. */ + {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, + {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10}, +/* mean values */ + {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */ + {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */ + {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */ + + {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */ + {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */ + {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10}, +/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */ + {} +}; + static const u8 sp80708_sensor_init[][8] = { {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10}, @@ -883,7 +973,9 @@ static const u8 sp80708_sensor_init[][8] = { {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10}, - /********/ + {} +}; +static const u8 sp80708_sensor_param1[][8] = { {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10}, {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10}, @@ -894,6 +986,19 @@ static const u8 sp80708_sensor_init[][8] = { {} }; +static const u8 (*sensor_init[10])[8] = { + hv7131r_sensor_init, /* HV7131R 0 */ + mi0360_sensor_init, /* MI0360 1 */ + mo4000_sensor_init, /* MO4000 2 */ + mt9v111_sensor_init, /* MT9V111 3 */ + om6802_sensor_init, /* OM6802 4 */ + ov7630_sensor_init, /* OV7630 5 */ + ov7648_sensor_init, /* OV7648 6 */ + ov7660_sensor_init, /* OV7660 7 */ + po1030_sensor_init, /* PO1030 8 */ + sp80708_sensor_init, /* SP80708 9 */ +}; + /* read bytes to gspca_dev->usb_buf */ static void reg_r(struct gspca_dev *gspca_dev, u16 value, int len) @@ -958,8 +1063,15 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) struct sd *sd = (struct sd *) gspca_dev; PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val); - gspca_dev->usb_buf[0] = 0x81 | (2 << 4); /* = a1 */ - gspca_dev->usb_buf[1] = sd->i2c_base; + switch (sd->sensor) { + case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */ + gspca_dev->usb_buf[0] = 0x80 | (2 << 4); + break; + default: /* i2c command = a1 (400 kHz) */ + gspca_dev->usb_buf[0] = 0x81 | (2 << 4); + break; + } + gspca_dev->usb_buf[1] = sd->i2c_addr; gspca_dev->usb_buf[2] = reg; gspca_dev->usb_buf[3] = val; gspca_dev->usb_buf[4] = 0; @@ -991,14 +1103,21 @@ static void i2c_w8(struct gspca_dev *gspca_dev, msleep(2); } -/* read 5 bytes in gspca_dev->usb_buf */ -static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg) +/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */ +static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len) { struct sd *sd = (struct sd *) gspca_dev; u8 mode[8]; - mode[0] = 0x81 | 0x10; - mode[1] = sd->i2c_base; + switch (sd->sensor) { + case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */ + mode[0] = 0x80 | 0x10; + break; + default: /* i2c command = 91 (400 kHz) */ + mode[0] = 0x81 | 0x10; + break; + } + mode[1] = sd->i2c_addr; mode[2] = reg; mode[3] = 0; mode[4] = 0; @@ -1007,33 +1126,43 @@ static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg) mode[7] = 0x10; i2c_w8(gspca_dev, mode); msleep(2); - mode[0] = 0x81 | (5 << 4) | 0x02; + mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02; mode[2] = 0; i2c_w8(gspca_dev, mode); msleep(2); reg_r(gspca_dev, 0x0a, 5); } -static int hv7131r_probe(struct gspca_dev *gspca_dev) +static void i2c_w_seq(struct gspca_dev *gspca_dev, + const u8 (*data)[8]) +{ + while ((*data)[0] != 0) { + if ((*data)[0] != 0xdd) + i2c_w8(gspca_dev, *data); + else + msleep((*data)[1]); + data++; + } +} + +static void hv7131r_probe(struct gspca_dev *gspca_dev) { i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */ msleep(10); reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */ msleep(10); - i2c_r5(gspca_dev, 0); /* read sensor id */ + i2c_r(gspca_dev, 0, 5); /* read sensor id */ if (gspca_dev->usb_buf[0] == 0x02 && gspca_dev->usb_buf[1] == 0x09 && gspca_dev->usb_buf[2] == 0x01 && gspca_dev->usb_buf[3] == 0x00 && gspca_dev->usb_buf[4] == 0x00) { - PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R"); - return 0; + PDEBUG(D_PROBE, "Sensor sn9c102P HV7131R found"); + return; } - PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x", + PDEBUG(D_PROBE, "Sensor 0x%02x 0x%02x 0x%02x - sn9c102P not found", gspca_dev->usb_buf[0], gspca_dev->usb_buf[1], gspca_dev->usb_buf[2]); - PDEBUG(D_PROBE, "Sensor sn9c102P Not found"); - return -ENODEV; } static void mi0360_probe(struct gspca_dev *gspca_dev) @@ -1075,7 +1204,6 @@ static void mi0360_probe(struct gspca_dev *gspca_dev) case 0x823a: PDEBUG(D_PROBE, "Sensor mt9v111"); sd->sensor = SENSOR_MT9V111; - sd->i2c_base = 0x5c; break; case 0x8243: PDEBUG(D_PROBE, "Sensor mi0360"); @@ -1086,7 +1214,42 @@ static void mi0360_probe(struct gspca_dev *gspca_dev) } } -static int configure_gpio(struct gspca_dev *gspca_dev, +static void ov7648_probe(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* check ov76xx */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x21; + i2c_r(gspca_dev, 0x0a, 2); + if (gspca_dev->usb_buf[3] == 0x76) { /* ov76xx */ + PDEBUG(D_PROBE, "Sensor ov%02x%02x", + gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); + return; + } + + /* reset */ + reg_w1(gspca_dev, 0x01, 0x29); + reg_w1(gspca_dev, 0x17, 0x42); + + /* check po1030 */ + reg_w1(gspca_dev, 0x17, 0x62); + reg_w1(gspca_dev, 0x01, 0x08); + sd->i2c_addr = 0x6e; + i2c_r(gspca_dev, 0x00, 2); + if (gspca_dev->usb_buf[3] == 0x10 /* po1030 */ + && gspca_dev->usb_buf[4] == 0x30) { + PDEBUG(D_PROBE, "Sensor po1030"); + sd->sensor = SENSOR_PO1030; + return; + } + + PDEBUG(D_PROBE, "Unknown sensor %02x%02x", + gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]); +} + +static void bridge_init(struct gspca_dev *gspca_dev, const u8 *sn9c1xx) { struct sd *sd = (struct sd *) gspca_dev; @@ -1103,9 +1266,10 @@ static int configure_gpio(struct gspca_dev *gspca_dev, /* configure gpio */ reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2); reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2); - reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm len was 3 */ + reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); switch (sd->sensor) { case SENSOR_OV7660: + case SENSOR_PO1030: case SENSOR_SP80708: reg9a = reg9a_spec; break; @@ -1115,7 +1279,7 @@ static int configure_gpio(struct gspca_dev *gspca_dev, } reg_w(gspca_dev, 0x9a, reg9a, 6); - reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); /*fixme:jfm was 60 only*/ + reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f); @@ -1127,10 +1291,22 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x40); break; case SENSOR_OM6802: - reg_w1(gspca_dev, 0x02, 0x71); - reg_w1(gspca_dev, 0x01, 0x42); + msleep(10); + reg_w1(gspca_dev, 0x02, 0x73); + reg_w1(gspca_dev, 0x17, 0x60); + reg_w1(gspca_dev, 0x01, 0x22); + msleep(100); + reg_w1(gspca_dev, 0x01, 0x62); + reg_w1(gspca_dev, 0x17, 0x64); reg_w1(gspca_dev, 0x17, 0x64); reg_w1(gspca_dev, 0x01, 0x42); + msleep(10); + reg_w1(gspca_dev, 0x01, 0x42); + i2c_w8(gspca_dev, om6802_init0[0]); + i2c_w8(gspca_dev, om6802_init0[1]); + msleep(15); + reg_w1(gspca_dev, 0x02, 0x71); + msleep(150); break; case SENSOR_OV7630: reg_w1(gspca_dev, 0x01, 0x61); @@ -1144,7 +1320,14 @@ static int configure_gpio(struct gspca_dev *gspca_dev, reg_w1(gspca_dev, 0x01, 0x62); reg_w1(gspca_dev, 0x01, 0x42); break; + case SENSOR_PO1030: + reg_w1(gspca_dev, 0x01, 0x61); + reg_w1(gspca_dev, 0x17, 0x20); + reg_w1(gspca_dev, 0x01, 0x60); + reg_w1(gspca_dev, 0x01, 0x40); + break; case SENSOR_OV7660: + /* fall thru */ case SENSOR_SP80708: reg_w1(gspca_dev, 0x01, 0x63); reg_w1(gspca_dev, 0x17, 0x20); @@ -1153,143 +1336,18 @@ static int configure_gpio(struct gspca_dev *gspca_dev, msleep(100); reg_w1(gspca_dev, 0x02, 0x62); break; + default: /* case SENSOR_HV7131R: */ /* case SENSOR_MI0360: */ /* case SENSOR_MO4000: */ - default: reg_w1(gspca_dev, 0x01, 0x43); reg_w1(gspca_dev, 0x17, 0x61); reg_w1(gspca_dev, 0x01, 0x42); - if (sd->sensor == SENSOR_HV7131R) { - if (hv7131r_probe(gspca_dev) < 0) - return -ENODEV; - } + if (sd->sensor == SENSOR_HV7131R + && sd->bridge == BRIDGE_SN9C102P) + hv7131r_probe(gspca_dev); break; } - return 0; -} - -static void hv7131R_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - static const u8 SetSensorClk[] = /* 0x08 Mclk */ - { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 }; - - while (hv7131r_sensor_init[i][0]) { - i2c_w8(gspca_dev, hv7131r_sensor_init[i]); - i++; - } - i2c_w8(gspca_dev, SetSensorClk); -} - -static void mi0360_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (mi0360_sensor_init[i][0]) { - i2c_w8(gspca_dev, mi0360_sensor_init[i]); - i++; - } -} - -static void mo4000_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (mo4000_sensor_init[i][0]) { - i2c_w8(gspca_dev, mo4000_sensor_init[i]); - i++; - } -} - -static void mt9v111_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, mt9v111_sensor_init[i]); - i++; - msleep(20); - while (mt9v111_sensor_init[i][0]) { - i2c_w8(gspca_dev, mt9v111_sensor_init[i]); - i++; - } -} - -static void om6802_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - while (om6802_sensor_init[i][0]) { - i2c_w8(gspca_dev, om6802_sensor_init[i]); - i++; - } -} - -static void ov7630_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 76 01 */ - i++; - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 (RGB+SRST) */ - i++; - msleep(20); - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ - i++; - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 */ - i++; - msleep(20); - i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */ - i++; -/*jfm:win i2c_r from 00 to 80*/ - - while (ov7630_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7630_sensor_init[i]); - i++; - } -} - -static void ov7648_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7648_sensor_init[i]); - i++; -/* win: dble reset */ - i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */ - i++; - msleep(20); -/* win: i2c reg read 00..7f */ - while (ov7648_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7648_sensor_init[i]); - i++; - } -} - -static void ov7660_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, ov7660_sensor_init[i]); /* reset SCCB */ - i++; - msleep(20); - while (ov7660_sensor_init[i][0]) { - i2c_w8(gspca_dev, ov7660_sensor_init[i]); - i++; - } -} - -static void sp80708_InitSensor(struct gspca_dev *gspca_dev) -{ - int i = 0; - - i2c_w8(gspca_dev, sp80708_sensor_init[i]); /* reset SCCB */ - i++; - msleep(20); - while (sp80708_sensor_init[i][0]) { - i2c_w8(gspca_dev, sp80708_sensor_init[i]); - i++; - } } /* this function is called at probe time */ @@ -1305,8 +1363,7 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->npkt = 24; /* 24 packets per ISOC message */ sd->bridge = id->driver_info >> 16; - sd->sensor = id->driver_info >> 8; - sd->i2c_base = id->driver_info; + sd->sensor = id->driver_info; sd->brightness = BRIGHTNESS_DEF; sd->contrast = CONTRAST_DEF; @@ -1322,7 +1379,6 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->quality = QUALITY_DEF; sd->jpegqual = 80; - gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; return 0; } @@ -1330,6 +1386,7 @@ static int sd_config(struct gspca_dev *gspca_dev, static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + const u8 *sn9c1xx; u8 regGpio[] = { 0x29, 0x74 }; u8 regF1; @@ -1356,8 +1413,14 @@ static int sd_init(struct gspca_dev *gspca_dev) case BRIDGE_SN9C120: if (regF1 != 0x12) return -ENODEV; - if (sd->sensor == SENSOR_MI0360) + switch (sd->sensor) { + case SENSOR_MI0360: mi0360_probe(gspca_dev); + break; + case SENSOR_OV7648: + ov7648_probe(gspca_dev); + break; + } regGpio[1] = 0x70; reg_w(gspca_dev, 0x01, regGpio, 2); break; @@ -1372,6 +1435,12 @@ static int sd_init(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0xf1, 0x01); + /* set the i2c address */ + sn9c1xx = sn_tb[sd->sensor]; + sd->i2c_addr = sn9c1xx[9]; + + gspca_dev->ctrl_dis = ctrl_dis[sd->sensor]; + return 0; } @@ -1383,7 +1452,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, switch (sd->sensor) { case SENSOR_HV7131R: { u8 Expodoit[] = - { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 }; + { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 }; Expodoit[3] = expo >> 16; Expodoit[4] = expo >> 8; @@ -1393,7 +1462,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, } case SENSOR_MI0360: { u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */ - { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 }; + { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 }; static const u8 doit[] = /* update sensor */ { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 }; static const u8 sensorgo[] = /* sensor on */ @@ -1412,9 +1481,9 @@ static u32 setexposure(struct gspca_dev *gspca_dev, } case SENSOR_MO4000: { u8 expoMof[] = - { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 }; + { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 }; u8 expoMo10[] = - { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 }; + { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 }; static const u8 gainMo[] = { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d }; @@ -1450,6 +1519,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, case SENSOR_OM6802: { u8 gainOm[] = { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 }; + /* preset AGC - works when AutoExpo = off */ if (expo > 0x03ff) expo = 0x03ff; @@ -1457,7 +1527,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev, expo = 0x0001; gainOm[3] = expo >> 2; i2c_w8(gspca_dev, gainOm); - reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f); + reg_w1(gspca_dev, 0x96, expo >> 5); PDEBUG(D_FRAM, "set exposure %d", gainOm[3]); break; } @@ -1489,7 +1559,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) case SENSOR_MT9V111: expo = sd->brightness >> 8; sd->exposure = setexposure(gspca_dev, expo); - break; + return; /* don't set the Y offset */ case SENSOR_OM6802: expo = sd->brightness >> 6; sd->exposure = setexposure(gspca_dev, expo); @@ -1497,8 +1567,7 @@ static void setbrightness(struct gspca_dev *gspca_dev) break; } - if (sd->sensor != SENSOR_MT9V111) - reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ + reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */ } static void setcontrast(struct gspca_dev *gspca_dev) @@ -1526,6 +1595,7 @@ static void setcolors(struct gspca_dev *gspca_dev) -24, -38, 64, /* UR UG UB */ 62, -51, -9 /* VR VG VB */ }; + for (i = 0; i < 6; i++) { v = uv[i] * sd->colors / COLOR_DEF; reg8a[i * 2] = v; @@ -1605,6 +1675,8 @@ static void setvflip(struct sd *sd) { u8 comn; + if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX)) + return; if (sd->sensor == SENSOR_OV7630) { comn = 0x02; if (!sd->vflip) @@ -1726,8 +1798,9 @@ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; int i; - u8 reg1, reg17; + u8 reg1, reg2, reg17; const u8 *sn9c1xx; + const u8 (*init)[8]; int mode; static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f }; static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec }; @@ -1743,8 +1816,26 @@ static int sd_start(struct gspca_dev *gspca_dev) 0x21); /* JPEG 422 */ jpeg_set_qual(sd->jpeg_hdr, sd->quality); - sn9c1xx = sn_tb[(int) sd->sensor]; - configure_gpio(gspca_dev, sn9c1xx); + /* initialize the bridge */ + sn9c1xx = sn_tb[sd->sensor]; + bridge_init(gspca_dev, sn9c1xx); + + /* initialize the sensor */ + i2c_w_seq(gspca_dev, sensor_init[sd->sensor]); + + switch (sd->sensor) { + case SENSOR_OM6802: + reg2 = 0x71; + break; + case SENSOR_SP80708: + reg2 = 0x62; + break; + default: + reg2 = 0x40; + break; + } + reg_w1(gspca_dev, 0x02, reg2); + reg_w1(gspca_dev, 0x02, reg2); reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]); reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]); @@ -1771,6 +1862,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7660: reg17 = 0xa0; break; + case SENSOR_PO1030: + reg17 = 0xa0; + break; default: reg17 = 0x60; break; @@ -1791,6 +1885,10 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x9a, 0x07); reg_w1(gspca_dev, 0x99, 0x59); break; + case SENSOR_OM6802: + reg_w1(gspca_dev, 0x9a, 0x08); + reg_w1(gspca_dev, 0x99, 0x10); + break; case SENSOR_OV7648: reg_w1(gspca_dev, 0x9a, 0x0a); reg_w1(gspca_dev, 0x99, 0x60); @@ -1806,21 +1904,20 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } - mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv; + reg_w(gspca_dev, 0x84, reg84, sizeof reg84); + reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */ + reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */ + reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */ + + init = NULL; + mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; if (mode) reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */ else reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */ reg17 = 0x61; /* 0x:20: enable sensor clock */ switch (sd->sensor) { - case SENSOR_HV7131R: - hv7131R_InitSensor(gspca_dev); - break; - case SENSOR_MI0360: - mi0360_InitSensor(gspca_dev); - break; case SENSOR_MO4000: - mo4000_InitSensor(gspca_dev); if (mode) { /* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */ reg1 = 0x06; /* clk 24Mz */ @@ -1830,7 +1927,7 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; case SENSOR_MT9V111: - mt9v111_InitSensor(gspca_dev); + init = mt9v111_sensor_param1; if (mode) { reg1 = 0x04; /* 320 clk 48Mhz */ } else { @@ -1839,22 +1936,21 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; case SENSOR_OM6802: - om6802_InitSensor(gspca_dev); + init = om6802_sensor_param1; reg17 = 0x64; /* 640 MCKSIZE */ break; case SENSOR_OV7630: - ov7630_InitSensor(gspca_dev); setvflip(sd); reg17 = 0xe2; reg1 = 0x44; break; case SENSOR_OV7648: - ov7648_InitSensor(gspca_dev); + init = ov7648_sensor_param1; reg17 = 0x21; /* reg1 = 0x42; * 42 - 46? */ break; case SENSOR_OV7660: - ov7660_InitSensor(gspca_dev); + init = ov7660_sensor_param1; if (sd->bridge == BRIDGE_SN9C120) { if (mode) { /* 320x240 - 160x120 */ reg17 = 0xa2; @@ -1866,9 +1962,14 @@ static int sd_start(struct gspca_dev *gspca_dev) * inverse power down */ } break; + case SENSOR_PO1030: + init = po1030_sensor_param1; + reg17 = 0xa2; + reg1 = 0x44; + break; default: /* case SENSOR_SP80708: */ - sp80708_InitSensor(gspca_dev); + init = sp80708_sensor_param1; if (mode) { /*?? reg1 = 0x04; * 320 clk 48Mhz */ } else { @@ -1877,6 +1978,13 @@ static int sd_start(struct gspca_dev *gspca_dev) } break; } + + /* more sensor initialization - param1 */ + if (init != NULL) { + i2c_w_seq(gspca_dev, init); +/* init = NULL; */ + } + reg_w(gspca_dev, 0xc0, C0, 6); reg_w(gspca_dev, 0xca, CA, 4); switch (sd->sensor) { @@ -1891,6 +1999,7 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } + /* here change size mode 0 -> VGA; 1 -> CIF */ sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40; reg_w1(gspca_dev, 0x18, sd->reg18); @@ -1898,6 +2007,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x17, reg17); reg_w1(gspca_dev, 0x01, reg1); + switch (sd->sensor) { case SENSOR_OV7630: setvflip(sd); @@ -1937,14 +2047,11 @@ static void sd_stopN(struct gspca_dev *gspca_dev) /* fall thru */ case SENSOR_MT9V111: case SENSOR_OV7630: + case SENSOR_PO1030: data = 0x29; break; - default: -/* case SENSOR_MO4000: */ -/* case SENSOR_OV7660: */ - break; } - sn9c1xx = sn_tb[(int) sd->sensor]; + sn9c1xx = sn_tb[sd->sensor]; reg_w1(gspca_dev, 0x01, sn9c1xx[1]); reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]); reg_w1(gspca_dev, 0x01, sn9c1xx[1]); @@ -1987,11 +2094,19 @@ static void do_autogain(struct gspca_dev *gspca_dev) sd->exposure = setexposure(gspca_dev, (unsigned int) (expotimes << 8)); break; + case SENSOR_OM6802: + expotimes = sd->exposure; + expotimes += (luma_mean - delta) >> 2; + if (expotimes < 0) + expotimes = 0; + sd->exposure = setexposure(gspca_dev, + (unsigned int) expotimes); + setredblue(gspca_dev); + break; default: /* case SENSOR_MO4000: */ /* case SENSOR_MI0360: */ /* case SENSOR_MT9V111: */ -/* case SENSOR_OM6802: */ expotimes = sd->exposure; expotimes += (luma_mean - delta) >> 6; if (expotimes < 0) @@ -2007,7 +2122,6 @@ static void do_autogain(struct gspca_dev *gspca_dev) /* scan the URB packets */ /* This function is run at interrupt level. */ static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -2019,7 +2133,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* end of frame */ gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, sof + 2); + data, sof + 2); if (sd->ag_cnt < 0) return; /* w1 w2 w3 */ @@ -2042,10 +2156,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (gspca_dev->last_packet_type == LAST_PACKET) { /* put the JPEG 422 header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) @@ -2295,69 +2409,69 @@ static const struct sd_desc sd_desc = { }; /* -- module initialisation -- */ -#define BSI(bridge, sensor, i2c_addr) \ +#define BS(bridge, sensor) \ .driver_info = (BRIDGE_ ## bridge << 16) \ - | (SENSOR_ ## sensor << 8) \ - | (i2c_addr) + | SENSOR_ ## sensor static const __devinitdata struct usb_device_id device_table[] = { #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)}, - {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)}, + {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)}, + {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)}, #endif - {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)}, - {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)}, - {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)}, - {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x06f8, 0x3008), BSI(SN9C105, OV7660, 0x21)}, - {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)}, -/* bw600.inf: - {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */ -/* {USB_DEVICE(0x0c45, 0x603a), BSI(SN9C102P, OV7648, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x607a), BSI(SN9C102P, OV7648, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)}, -/* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)}, -/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6802, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)}, + {USB_DEVICE(0x045e, 0x00f5), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x045e, 0x00f7), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)}, + {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)}, + {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)}, +/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */ + {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)}, +/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */ +/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */ + {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)}, +/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */ + {USB_DEVICE(0x0c45, 0x60c0), BS(SN9C105, MI0360)}, +/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */ +/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */ +/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */ + {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)}, +/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */ +/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */ + {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)}, - {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)}, + {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)}, + {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)}, #endif - {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */ -/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */ - {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/ - {USB_DEVICE(0x0c45, 0x610e), BSI(SN9C120, OV7630, 0x21)}, /*sn9c128*/ -/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */ -/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */ - {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/ +/* {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, P1030xC)}, */ +/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */ + {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/ + {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/ +/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */ +/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */ +/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */ + {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/ /*bw600.inf:*/ - {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/ - {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)}, - {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */ + {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/ + {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)}, + {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)}, +/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)}, + {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)}, #endif -/* {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */ - {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)}, - {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)}, +/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */ + {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)}, + {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)}, #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE - {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)}, + {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)}, #endif - {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)}, - {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)}, -/* {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/ - {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/ - {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)}, + {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)}, +/* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/ + {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/ {} }; MODULE_DEVICE_TABLE(usb, device_table); diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c index 7dbd5eea6cc08..fe46868a87f2e 100644 --- a/drivers/media/video/gspca/spca500.c +++ b/drivers/media/video/gspca/spca500.c @@ -899,8 +899,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -913,11 +912,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* gspca_dev->last_packet_type = DISCARD_PACKET; */ return; } - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, ffd9, 2); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); data += SPCA500_OFFSET_DATA; @@ -931,7 +930,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, i = 0; do { if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, i + 1); len -= i; data += i; @@ -940,7 +939,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } i++; } while (i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static void setbrightness(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c index 66f9f00561468..6761a3048a98a 100644 --- a/drivers/media/video/gspca/spca501.c +++ b/drivers/media/video/gspca/spca501.c @@ -2032,20 +2032,15 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA501_OFFSET_DATA; len -= SPCA501_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ @@ -2053,8 +2048,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } data++; len--; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c index ea8c9fe2e9619..0f9232ff12815 100644 --- a/drivers/media/video/gspca/spca505.c +++ b/drivers/media/video/gspca/spca505.c @@ -739,26 +739,22 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ break; default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c index a199298a6419a..ab28cc23e4158 100644 --- a/drivers/media/video/gspca/spca506.c +++ b/drivers/media/video/gspca/spca506.c @@ -543,18 +543,15 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA50X_OFFSET_DATA; len -= SPCA50X_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ /* gspca_dev->last_packet_type = DISCARD_PACKET; */ @@ -562,8 +559,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c index 9696c4caf5c93..4d8e6cf75d555 100644 --- a/drivers/media/video/gspca/spca508.c +++ b/drivers/media/video/gspca/spca508.c @@ -1447,26 +1447,22 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { switch (data[0]) { case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += SPCA508_OFFSET_DATA; len -= SPCA508_OFFSET_DATA; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); break; case 0xff: /* drop */ break; default: data += 1; len -= 1; - gspca_frame_add(gspca_dev, INTER_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); break; } } diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c index 27e82b35f3e7f..58c2f0039af1f 100644 --- a/drivers/media/video/gspca/spca561.c +++ b/drivers/media/video/gspca/spca561.c @@ -779,8 +779,7 @@ static void do_autogain(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -788,12 +787,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, len--; switch (*data++) { /* sequence number */ case 0: /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); if (data[1] & 0x10) { /* compressed bayer */ - gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } else { /* raw bayer (with a header, which we skip) */ if (sd->chip_revision == Rev012A) { @@ -803,14 +800,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += 16; len -= 16; } - gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); } return; case 0xff: /* drop (empty mpackets) */ return; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } /* rev 72a only */ diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 715a68f0156eb..1fcaca6a87f7f 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -168,18 +168,22 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev) * request and read a block of data - see warning on sq905_command. */ static int -sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size) +sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock) { int ret; int act_len; gspca_dev->usb_buf[0] = '\0'; + if (need_lock) + mutex_lock(&gspca_dev->usb_lock); ret = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), USB_REQ_SYNCH_FRAME, /* request */ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, SQ905_BULK_READ, size, gspca_dev->usb_buf, 1, SQ905_CMD_TIMEOUT); + if (need_lock) + mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) { PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret); return ret; @@ -210,11 +214,9 @@ static void sq905_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int bytes_left; /* bytes remaining in current frame. */ int data_len; /* size to use for the next read. */ int header_read; /* true if we have already read the frame header. */ - int discarding; /* true if we failed to get space for frame. */ int packet_type; int frame_sz; int ret; @@ -222,7 +224,6 @@ static void sq905_dostream(struct work_struct *work) u8 *buffer; buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA); - mutex_lock(&gspca_dev->usb_lock); if (!buffer) { PDEBUG(D_ERR, "Couldn't allocate USB buffer"); goto quit_stream; @@ -232,28 +233,22 @@ static void sq905_dostream(struct work_struct *work) + FRAME_HEADER_LEN; while (gspca_dev->present && gspca_dev->streaming) { - /* Need a short delay to ensure streaming flag was set by - * gspca and to make sure gspca can grab the mutex. */ - mutex_unlock(&gspca_dev->usb_lock); - msleep(1); - /* request some data and then read it until we have * a complete frame. */ bytes_left = frame_sz; header_read = 0; - discarding = 0; - while (bytes_left > 0) { + /* Note we do not check for gspca_dev->streaming here, as + we must finish reading an entire frame, otherwise the + next time we stream we start reading in the middle of a + frame. */ + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > SQ905_MAX_TRANSFER ? SQ905_MAX_TRANSFER : bytes_left; - mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present) - goto quit_stream; - ret = sq905_read_data(gspca_dev, buffer, data_len); + ret = sq905_read_data(gspca_dev, buffer, data_len, 1); if (ret < 0) goto quit_stream; - mutex_unlock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, + PDEBUG(D_PACK, "Got %d bytes out of %d for frame", data_len, bytes_left); bytes_left -= data_len; @@ -270,34 +265,30 @@ static void sq905_dostream(struct work_struct *work) } else { packet_type = INTER_PACKET; } - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) { - frame = gspca_frame_add(gspca_dev, packet_type, - frame, data, data_len); - /* If entire frame fits in one packet we still - need to add a LAST_PACKET */ - if (packet_type == FIRST_PACKET && - bytes_left == 0) - frame = gspca_frame_add(gspca_dev, - LAST_PACKET, - frame, data, 0); - } else { - discarding = 1; - } + gspca_frame_add(gspca_dev, packet_type, + data, data_len); + /* If entire frame fits in one packet we still + need to add a LAST_PACKET */ + if (packet_type == FIRST_PACKET && + bytes_left == 0) + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + } + if (gspca_dev->present) { + /* acknowledge the frame */ + mutex_lock(&gspca_dev->usb_lock); + ret = sq905_ack_frame(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + if (ret < 0) + goto quit_stream; } - /* acknowledge the frame */ - mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present) - goto quit_stream; - ret = sq905_ack_frame(gspca_dev); - if (ret < 0) - goto quit_stream; } quit_stream: - /* the usb_lock is already acquired */ - if (gspca_dev->present) + if (gspca_dev->present) { + mutex_lock(&gspca_dev->usb_lock); sq905_command(gspca_dev, SQ905_CLEAR); - mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + } kfree(buffer); } @@ -346,7 +337,7 @@ static int sd_init(struct gspca_dev *gspca_dev) ret = sq905_command(gspca_dev, SQ905_ID); if (ret < 0) return ret; - ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4); + ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0); if (ret < 0) return ret; /* usb_buf is allocated with kmalloc so is aligned. diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index 9168925054328..d70b156872d6f 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -115,11 +115,9 @@ static void sq905c_dostream(struct work_struct *work) { struct sd *dev = container_of(work, struct sd, work_struct); struct gspca_dev *gspca_dev = &dev->gspca_dev; - struct gspca_frame *frame; int bytes_left; /* bytes remaining in current frame. */ int data_len; /* size to use for the next read. */ int act_len; - int discarding = 0; /* true if we failed to get space for frame. */ int packet_type; int ret; u8 *buffer; @@ -131,8 +129,6 @@ static void sq905c_dostream(struct work_struct *work) } while (gspca_dev->present && gspca_dev->streaming) { - if (!gspca_dev->present) - goto quit_stream; /* Request the header, which tells the size to download */ ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), @@ -149,17 +145,11 @@ static void sq905c_dostream(struct work_struct *work) PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left); /* We keep the header. It has other information, too. */ packet_type = FIRST_PACKET; - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) { - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, FRAME_HEADER_LEN); - } else - discarding = 1; - while (bytes_left > 0) { + gspca_frame_add(gspca_dev, packet_type, + buffer, FRAME_HEADER_LEN); + while (bytes_left > 0 && gspca_dev->present) { data_len = bytes_left > SQ905C_MAX_TRANSFER ? SQ905C_MAX_TRANSFER : bytes_left; - if (!gspca_dev->present) - goto quit_stream; ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), buffer, data_len, &act_len, @@ -174,19 +164,16 @@ static void sq905c_dostream(struct work_struct *work) packet_type = LAST_PACKET; else packet_type = INTER_PACKET; - frame = gspca_get_i_frame(gspca_dev); - if (frame && !discarding) - gspca_frame_add(gspca_dev, packet_type, - frame, buffer, data_len); - else - discarding = 1; + gspca_frame_add(gspca_dev, packet_type, + buffer, data_len); } } quit_stream: - mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->present) + if (gspca_dev->present) { + mutex_lock(&gspca_dev->usb_lock); sq905c_command(gspca_dev, SQ905C_CLEAR, 0); - mutex_unlock(&gspca_dev->usb_lock); + mutex_unlock(&gspca_dev->usb_lock); + } kfree(buffer); } diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c index 47628964801e6..8e23320d7ab7c 100644 --- a/drivers/media/video/gspca/stk014.c +++ b/drivers/media/video/gspca/stk014.c @@ -418,8 +418,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -435,11 +434,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * (without ending - ff d9) */ if (data[0] == 0xff && data[1] == 0xfe) { - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - ffd9, 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); /* put the JPEG 411 header */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); /* beginning of the frame */ @@ -447,7 +446,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += STKHDRSZ; len -= STKHDRSZ; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c new file mode 100644 index 0000000000000..2a69d7ccb50dd --- /dev/null +++ b/drivers/media/video/gspca/stv0680.c @@ -0,0 +1,364 @@ +/* + * STV0680 USB Camera Driver + * + * Copyright (C) 2009 Hans de Goede + * + * This module is adapted from the in kernel v4l1 stv680 driver: + * + * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net) + * + * Thanks to STMicroelectronics for information on the usb commands, and + * to Steve Miller at STM for his help and encouragement while I was + * writing this driver. + * + * 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, or + * (at your option) any later version. + * + * 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 + * + */ + +#define MODULE_NAME "stv0680" + +#include "gspca.h" + +MODULE_AUTHOR("Hans de Goede "); +MODULE_DESCRIPTION("STV0680 USB Camera Driver"); +MODULE_LICENSE("GPL"); + +/* specific webcam descriptor */ +struct sd { + struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_pix_format mode; + u8 orig_mode; + u8 video_mode; + u8 current_mode; +}; + +/* V4L2 controls supported by the driver */ +static struct ctrl sd_ctrls[] = { +}; + +static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val, + int size) +{ + int ret = -1; + u8 req_type = 0; + + switch (set) { + case 0: /* 0xc1 */ + req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + break; + case 1: /* 0x41 */ + req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT; + break; + case 2: /* 0x80 */ + req_type = USB_DIR_IN | USB_RECIP_DEVICE; + break; + case 3: /* 0x40 */ + req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE; + break; + } + + ret = usb_control_msg(gspca_dev->dev, + usb_rcvctrlpipe(gspca_dev->dev, 0), + req, req_type, + val, 0, gspca_dev->usb_buf, size, 500); + + if ((ret < 0) && (req != 0x0a)) + PDEBUG(D_ERR, + "usb_control_msg error %i, request = 0x%x, error = %i", + set, req, ret); + + return ret; +} + +static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret) +{ + stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */ + PDEBUG(D_ERR, "last error: %i, command = 0x%x", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + return ret; +} + +static int stv0680_get_video_mode(struct gspca_dev *gspca_dev) +{ + /* Note not sure if this init of usb_buf is really necessary */ + memset(gspca_dev->usb_buf, 0, 8); + gspca_dev->usb_buf[0] = 0x0f; + + if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) { + PDEBUG(D_ERR, "Get_Camera_Mode failed"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */ +} + +static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (sd->current_mode == mode) + return 0; + + memset(gspca_dev->usb_buf, 0, 8); + gspca_dev->usb_buf[0] = mode; + + if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) { + PDEBUG(D_ERR, "Set_Camera_Mode failed"); + return stv0680_handle_error(gspca_dev, -EIO); + } + + /* Verify we got what we've asked for */ + if (stv0680_get_video_mode(gspca_dev) != mode) { + PDEBUG(D_ERR, "Error setting camera video mode!"); + return -EIO; + } + + sd->current_mode = mode; + + return 0; +} + +/* this function is called at probe time */ +static int sd_config(struct gspca_dev *gspca_dev, + const struct usb_device_id *id) +{ + int ret; + struct sd *sd = (struct sd *) gspca_dev; + struct cam *cam = &gspca_dev->cam; + + /* ping camera to be sure STV0680 is present */ + if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 || + gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) { + PDEBUG(D_ERR, "STV(e): camera ping failed!!"); + return stv0680_handle_error(gspca_dev, -ENODEV); + } + + /* get camera descriptor */ + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09) + return stv0680_handle_error(gspca_dev, -ENODEV); + + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 || + gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) { + PDEBUG(D_ERR, "Could not get descriptor 0200."); + return stv0680_handle_error(gspca_dev, -ENODEV); + } + if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02) + return stv0680_handle_error(gspca_dev, -ENODEV); + if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24) + return stv0680_handle_error(gspca_dev, -ENODEV); + if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -ENODEV); + + if (!(gspca_dev->usb_buf[7] & 0x09)) { + PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode"); + return -ENODEV; + } + if (gspca_dev->usb_buf[7] & 0x01) + PDEBUG(D_PROBE, "Camera supports CIF mode"); + if (gspca_dev->usb_buf[7] & 0x02) + PDEBUG(D_PROBE, "Camera supports VGA mode"); + if (gspca_dev->usb_buf[7] & 0x08) + PDEBUG(D_PROBE, "Camera supports QVGA mode"); + + if (gspca_dev->usb_buf[7] & 0x01) + sd->video_mode = 0x00; /* CIF */ + else + sd->video_mode = 0x03; /* QVGA */ + + /* FW rev, ASIC rev, sensor ID */ + PDEBUG(D_PROBE, "Firmware rev is %i.%i", + gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]); + PDEBUG(D_PROBE, "ASIC rev is %i.%i", + gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]); + PDEBUG(D_PROBE, "Sensor ID is %i", + (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4)); + + + ret = stv0680_get_video_mode(gspca_dev); + if (ret < 0) + return ret; + sd->current_mode = sd->orig_mode = ret; + + ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); + if (ret < 0) + return ret; + + /* Get mode details */ + if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -EIO); + + cam->bulk = 1; + cam->bulk_nurbs = 1; /* The cam cannot handle more */ + cam->bulk_size = (gspca_dev->usb_buf[0] << 24) | + (gspca_dev->usb_buf[1] << 16) | + (gspca_dev->usb_buf[2] << 8) | + (gspca_dev->usb_buf[3]); + sd->mode.width = (gspca_dev->usb_buf[4] << 8) | + (gspca_dev->usb_buf[5]); /* 322, 356, 644 */ + sd->mode.height = (gspca_dev->usb_buf[6] << 8) | + (gspca_dev->usb_buf[7]); /* 242, 292, 484 */ + sd->mode.pixelformat = V4L2_PIX_FMT_STV0680; + sd->mode.field = V4L2_FIELD_NONE; + sd->mode.bytesperline = sd->mode.width; + sd->mode.sizeimage = cam->bulk_size; + sd->mode.colorspace = V4L2_COLORSPACE_SRGB; + + /* origGain = gspca_dev->usb_buf[12]; */ + + cam->cam_mode = &sd->mode; + cam->nmodes = 1; + + + ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode); + if (ret < 0) + return ret; + + if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 || + gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) { + PDEBUG(D_ERR, "Could not get descriptor 0100."); + return stv0680_handle_error(gspca_dev, -EIO); + } + + return 0; +} + +/* this function is called at probe and resume time */ +static int sd_init(struct gspca_dev *gspca_dev) +{ + return 0; +} + +/* -- start the camera -- */ +static int sd_start(struct gspca_dev *gspca_dev) +{ + int ret; + struct sd *sd = (struct sd *) gspca_dev; + + ret = stv0680_set_video_mode(gspca_dev, sd->video_mode); + if (ret < 0) + return ret; + + if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10) + return stv0680_handle_error(gspca_dev, -EIO); + + /* Start stream at: + 0x0000 = CIF (352x288) + 0x0100 = VGA (640x480) + 0x0300 = QVGA (320x240) */ + if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0) + return stv0680_handle_error(gspca_dev, -EIO); + + return 0; +} + +static void sd_stopN(struct gspca_dev *gspca_dev) +{ + /* This is a high priority command; it stops all lower order cmds */ + if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0) + stv0680_handle_error(gspca_dev, -EIO); +} + +static void sd_stop0(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (!sd->gspca_dev.present) + return; + + stv0680_set_video_mode(gspca_dev, sd->orig_mode); +} + +static void sd_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, + int len) +{ + struct sd *sd = (struct sd *) gspca_dev; + + /* Every now and then the camera sends a 16 byte packet, no idea + what it contains, but it is not image data, when this + happens the frame received before this packet is corrupt, + so discard it. */ + if (len != sd->mode.sizeimage) { + gspca_dev->last_packet_type = DISCARD_PACKET; + return; + } + + /* Finish the previous frame, we do this upon reception of the next + packet, even though it is already complete so that the strange 16 + byte packets send after a corrupt frame can discard it. */ + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); + + /* Store the just received frame */ + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); +} + +/* sub-driver description */ +static const struct sd_desc sd_desc = { + .name = MODULE_NAME, + .ctrls = sd_ctrls, + .nctrls = ARRAY_SIZE(sd_ctrls), + .config = sd_config, + .init = sd_init, + .start = sd_start, + .stopN = sd_stopN, + .stop0 = sd_stop0, + .pkt_scan = sd_pkt_scan, +}; + +/* -- module initialisation -- */ +static const __devinitdata struct usb_device_id device_table[] = { + {USB_DEVICE(0x0553, 0x0202)}, + {USB_DEVICE(0x041e, 0x4007)}, + {} +}; +MODULE_DEVICE_TABLE(usb, device_table); + +/* -- device connect -- */ +static int sd_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), + THIS_MODULE); +} + +static struct usb_driver sd_driver = { + .name = MODULE_NAME, + .id_table = device_table, + .probe = sd_probe, + .disconnect = gspca_disconnect, +#ifdef CONFIG_PM + .suspend = gspca_suspend, + .resume = gspca_resume, +#endif +}; + +/* -- module insert / remove -- */ +static int __init sd_mod_init(void) +{ + int ret; + ret = usb_register(&sd_driver); + if (ret < 0) + return ret; + PDEBUG(D_PROBE, "registered"); + return 0; +} +static void __exit sd_mod_exit(void) +{ + usb_deregister(&sd_driver); + PDEBUG(D_PROBE, "deregistered"); +} + +module_init(sd_mod_init); +module_exit(sd_mod_exit); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index bfae63f5584c4..5d0241bb16118 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -312,8 +312,7 @@ static void stv06xx_stopN(struct gspca_dev *gspca_dev) * The 0005 and 0100 chunks seem to appear only in compressed stream. */ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -366,7 +365,7 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, sd->to_skip -= skip; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, chunk_len); break; @@ -378,7 +377,7 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, /* Create a new frame, chunk length should be zero */ gspca_frame_add(gspca_dev, FIRST_PACKET, - frame, data, 0); + NULL, 0); if (sd->bridge == BRIDGE_ST6422) sd->to_skip = gspca_dev->width * 4; @@ -394,8 +393,8 @@ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev, PDEBUG(D_PACK, "End of frame detected"); /* Complete the last frame (if any) */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, - frame, data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); if (chunk_len) PDEBUG(D_ERR, "Chunk length is " diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c index 1a9af2ebdbefc..72bf3b4f0a316 100644 --- a/drivers/media/video/gspca/sunplus.c +++ b/drivers/media/video/gspca/sunplus.c @@ -1116,7 +1116,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -1186,11 +1185,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, break; } if (sof) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - ffd9, 2); + gspca_frame_add(gspca_dev, LAST_PACKET, + ffd9, 2); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); } @@ -1198,7 +1197,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, i = 0; do { if (data[i] == 0xff) { - gspca_frame_add(gspca_dev, INTER_PACKET, frame, + gspca_frame_add(gspca_dev, INTER_PACKET, data, i + 1); len -= i; data += i; @@ -1207,7 +1206,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, } i++; } while (i < len); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c index 1d321c30d22f5..55ef6a7444279 100644 --- a/drivers/media/video/gspca/t613.c +++ b/drivers/media/video/gspca/t613.c @@ -938,7 +938,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso packet length */ { @@ -956,9 +955,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* extra bytes....., could be processed too but would be * a waste of time, right now leave the application and * libjpeg do it for ourserlves.. */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, + gspca_frame_add(gspca_dev, LAST_PACKET, ffd9, 2); - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } @@ -967,7 +966,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * other's do not include it... */ len -= 2; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c index 4b44dde9f8b8f..b74a3b6489c77 100644 --- a/drivers/media/video/gspca/tv8532.c +++ b/drivers/media/video/gspca/tv8532.c @@ -398,8 +398,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ - __u8 *data, /* isoc packet */ + u8 *data, /* isoc packet */ int len) /* iso packet length */ { struct sd *sd = (struct sd *) gspca_dev; @@ -424,9 +423,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, * - 4 bytes */ gspca_frame_add(gspca_dev, packet_type0, - frame, data + 2, gspca_dev->width); + data + 2, gspca_dev->width); gspca_frame_add(gspca_dev, packet_type1, - frame, data + gspca_dev->width + 5, gspca_dev->width); + data + gspca_dev->width + 5, gspca_dev->width); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c index 589042f6adbe9..c090efcd80454 100644 --- a/drivers/media/video/gspca/vc032x.c +++ b/drivers/media/video/gspca/vc032x.c @@ -2987,7 +2987,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, /* target */ u8 *data, /* isoc packet */ int len) /* iso pkt length */ { @@ -2996,21 +2995,25 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (data[0] == 0xff && data[1] == 0xd8) { PDEBUG(D_PACK, "vc032x header packet found len %d", len); - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); data += sd->image_offset; len -= sd->image_offset; - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, - data, len); + gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); return; } /* The vc0321 sends some additional data after sending the complete * frame, we ignore this. */ - if (sd->bridge == BRIDGE_VC0321 - && len > frame->v4l2_buf.length - (frame->data_end - frame->data)) - len = frame->v4l2_buf.length - (frame->data_end - frame->data); - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + if (sd->bridge == BRIDGE_VC0321) { + struct gspca_frame *frame; + int l; + + frame = gspca_get_i_frame(gspca_dev); + l = frame->data_end - frame->data; + if (len > frame->v4l2_buf.length - l) + len = frame->v4l2_buf.length - l; + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val) @@ -3092,6 +3095,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev, switch (menu->id) { case V4L2_CID_POWER_LINE_FREQUENCY: + if (menu->index >= ARRAY_SIZE(freq_nm)) + break; strcpy((char *) menu->name, freq_nm[menu->index]); return 0; } diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c new file mode 100644 index 0000000000000..2fffe203bed87 --- /dev/null +++ b/drivers/media/video/gspca/w996Xcf.c @@ -0,0 +1,609 @@ +/** + * + * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip. + * + * Copyright (C) 2009 Hans de Goede + * + * This module is adapted from the in kernel v4l1 w9968cf driver: + * + * Copyright (C) 2002-2004 by Luca Risolia + * + * 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, or + * any later version. + * + * 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 + * + */ + +/* Note this is not a stand alone driver, it gets included in ov519.c, this + is a bit of a hack, but it needs the driver code for a lot of different + ov sensors which is already present in ov519.c (the old v4l1 driver used + the ovchipcam framework). When we have the time we really should move + the sensor drivers to v4l2 sub drivers, and properly split of this + driver from ov519.c */ + +/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */ +#define CONEX_CAM +#include "jpeg.h" + +#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */ + +#define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET) +#define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET) + +static const struct v4l2_pix_format w9968cf_vga_mode[] = { + {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 160 * 2, + .sizeimage = 160 * 120 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, + .bytesperline = 176 * 2, + .sizeimage = 176 * 144 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 320 * 2, + .sizeimage = 320 * 240 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 352 * 2, + .sizeimage = 352 * 288 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, + {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, + .bytesperline = 640 * 2, + .sizeimage = 640 * 480 * 2, + .colorspace = V4L2_COLORSPACE_JPEG}, +}; + +static int reg_w(struct sd *sd, __u16 index, __u16 value); + +/*-------------------------------------------------------------------------- + Write 64-bit data to the fast serial bus registers. + Return 0 on success, -1 otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_fsb(struct sd *sd, u16* data) +{ + struct usb_device* udev = sd->gspca_dev.dev; + u16 value; + int ret; + + value = *data++; + memcpy(sd->gspca_dev.usb_buf, data, 6); + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + value, 0x06, sd->gspca_dev.usb_buf, 6, 500); + if (ret < 0) { + PDEBUG(D_ERR, "Write FSB registers failed (%d)", ret); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------- + Write data to the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_write_sb(struct sd *sd, u16 value) +{ + int ret; + + /* We don't use reg_w here, as that would cause all writes when + bitbanging i2c to be logged, making the logs impossible to read */ + ret = usb_control_msg(sd->gspca_dev.dev, + usb_sndctrlpipe(sd->gspca_dev.dev, 0), + 0, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, 0x01, NULL, 0, 500); + + udelay(W9968CF_I2C_BUS_DELAY); + + if (ret < 0) { + PDEBUG(D_ERR, "Write SB reg [01] %04x failed", value); + return ret; + } + + return 0; +} + +/*-------------------------------------------------------------------------- + Read data from the serial bus control register. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_read_sb(struct sd *sd) +{ + int ret; + + /* We don't use reg_r here, as the w9968cf is special and has 16 + bit registers instead of 8 bit */ + ret = usb_control_msg(sd->gspca_dev.dev, + usb_rcvctrlpipe(sd->gspca_dev.dev, 0), + 1, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, 0x01, sd->gspca_dev.usb_buf, 2, 500); + if (ret >= 0) + ret = sd->gspca_dev.usb_buf[0] | + (sd->gspca_dev.usb_buf[1] << 8); + else + PDEBUG(D_ERR, "Read SB reg [01] failed"); + + udelay(W9968CF_I2C_BUS_DELAY); + + return ret; +} + +/*-------------------------------------------------------------------------- + Upload quantization tables for the JPEG compression. + This function is called by w9968cf_start_transfer(). + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_upload_quantizationtables(struct sd *sd) +{ + u16 a, b; + int ret = 0, i, j; + + ret += reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */ + + for (i = 0, j = 0; i < 32; i++, j += 2) { + a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8); + b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8); + ret += reg_w(sd, 0x40+i, a); + ret += reg_w(sd, 0x60+i, b); + } + ret += reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */ + + return ret; +} + +/**************************************************************************** + * Low-level I2C I/O functions. * + * The adapter supports the following I2C transfer functions: * + * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) * + * i2c_adap_read_byte_data() * + * i2c_adap_read_byte() * + ****************************************************************************/ + +static int w9968cf_smbus_start(struct sd *sd) +{ + int ret = 0; + + ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + + return ret; +} + +static int w9968cf_smbus_stop(struct sd *sd) +{ + int ret = 0; + + ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + + return ret; +} + +static int w9968cf_smbus_write_byte(struct sd *sd, u8 v) +{ + u8 bit; + int ret = 0, sda; + + for (bit = 0 ; bit < 8 ; bit++) { + sda = (v & 0x80) ? 2 : 0; + v <<= 1; + /* SDE=1, SDA=sda, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x10 | sda); + /* SDE=1, SDA=sda, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x11 | sda); + /* SDE=1, SDA=sda, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x10 | sda); + } + + return ret; +} + +static int w9968cf_smbus_read_byte(struct sd *sd, u8* v) +{ + u8 bit; + int ret = 0; + + /* No need to ensure SDA is high as we are always called after + read_ack which ends with SDA high */ + *v = 0; + for (bit = 0 ; bit < 8 ; bit++) { + *v <<= 1; + /* SDE=1, SDA=1, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0013); + *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0; + /* SDE=1, SDA=1, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0012); + } + + return ret; +} + +static int w9968cf_smbus_write_nack(struct sd *sd) +{ + int ret = 0; + + /* No need to ensure SDA is high as we are always called after + read_byte which ends with SDA high */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + + return ret; +} + +static int w9968cf_smbus_read_ack(struct sd *sd) +{ + int ret = 0, sda; + + /* Ensure SDA is high before raising clock to avoid a spurious stop */ + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */ + sda = w9968cf_read_sb(sd); + ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */ + if (sda < 0) + ret += sda; + else if (sda & 0x08) { + PDEBUG(D_USBI, "Did not receive i2c ACK"); + ret += -1; + } + + return ret; +} + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */ +static int w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value) +{ + u16* data = (u16 *)sd->gspca_dev.usb_buf; + int ret = 0; + + data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0); + data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0; + data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0); + data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0; + data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0; + data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0); + data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0; + data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0; + data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0); + data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0; + + ret += w9968cf_write_fsb(sd, data); + + data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0); + data[0] |= (reg & 0x40) ? 0x0540 : 0x0; + data[0] |= (reg & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0); + data[1] |= (reg & 0x10) ? 0x0054 : 0x0; + data[1] |= (reg & 0x08) ? 0x1500 : 0x0; + data[1] |= (reg & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0); + data[2] |= (reg & 0x02) ? 0x0150 : 0x0; + data[2] |= (reg & 0x01) ? 0x5400 : 0x0; + data[3] = 0x001d; + + ret += w9968cf_write_fsb(sd, data); + + data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0); + data[0] |= (value & 0x40) ? 0x0540 : 0x0; + data[0] |= (value & 0x20) ? 0x5000 : 0x0; + data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0); + data[1] |= (value & 0x10) ? 0x0054 : 0x0; + data[1] |= (value & 0x08) ? 0x1500 : 0x0; + data[1] |= (value & 0x04) ? 0x4000 : 0x0; + data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0); + data[2] |= (value & 0x02) ? 0x0150 : 0x0; + data[2] |= (value & 0x01) ? 0x5400 : 0x0; + data[3] = 0xfe1d; + + ret += w9968cf_write_fsb(sd, data); + + if (!ret) + PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg); + else + PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg); + + return ret; +} + +/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */ +static int w9968cf_i2c_r(struct sd *sd, u8 reg) +{ + int ret = 0; + u8 value; + + /* Fast serial bus data control disable */ + ret += w9968cf_write_sb(sd, 0x0013); /* don't change ! */ + + ret += w9968cf_smbus_start(sd); + ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_write_byte(sd, reg); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_stop(sd); + ret += w9968cf_smbus_start(sd); + ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1); + ret += w9968cf_smbus_read_ack(sd); + ret += w9968cf_smbus_read_byte(sd, &value); + /* signal we don't want to read anymore, the v4l1 driver used to + send an ack here which is very wrong! (and then fixed + the issues this gave by retrying reads) */ + ret += w9968cf_smbus_write_nack(sd); + ret += w9968cf_smbus_stop(sd); + + /* Fast serial bus data control re-enable */ + ret += w9968cf_write_sb(sd, 0x0030); + + if (!ret) { + ret = value; + PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value); + } else + PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg); + + return ret; +} + + +/*-------------------------------------------------------------------------- + Turn on the LED on some webcams. A beep should be heard too. + Return 0 on success, a negative number otherwise. + --------------------------------------------------------------------------*/ +static int w9968cf_configure(struct sd *sd) +{ + int ret = 0; + + ret += reg_w(sd, 0x00, 0xff00); /* power-down */ + ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */ + ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */ + ret += reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */ + ret += reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */ + ret += reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */ + ret += reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */ + + if (ret) + PDEBUG(D_ERR, "Couldn't turn on the LED"); + + sd->stopped = 1; + + return ret; +} + +static int w9968cf_init(struct sd *sd) +{ + int ret = 0; + unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2), + y0 = 0x0000, + u0 = y0 + hw_bufsize/2, + v0 = u0 + hw_bufsize/4, + y1 = v0 + hw_bufsize/4, + u1 = y1 + hw_bufsize/2, + v1 = u1 + hw_bufsize/4; + + ret += reg_w(sd, 0x00, 0xff00); /* power off */ + ret += reg_w(sd, 0x00, 0xbf10); /* power on */ + + ret += reg_w(sd, 0x03, 0x405d); /* DRAM timings */ + ret += reg_w(sd, 0x04, 0x0030); /* SDRAM timings */ + + ret += reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */ + ret += reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */ + ret += reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */ + ret += reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */ + ret += reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */ + ret += reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */ + + ret += reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */ + ret += reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */ + ret += reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */ + ret += reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */ + ret += reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */ + ret += reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */ + + ret += reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */ + ret += reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */ + + ret += reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */ + ret += reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */ + + ret += reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */ + ret += reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/ + ret += reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */ + ret += reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */ + + return ret; +} + +static int w9968cf_set_crop_window(struct sd *sd) +{ + int ret = 0, start_cropx, start_cropy, x, y, fw, fh, cw, ch, + max_width, max_height; + + if (sd->sif) { + max_width = 352; + max_height = 288; + } else { + max_width = 640; + max_height = 480; + } + + if (sd->sensor == SEN_OV7620) { + /* Sigh, this is dependend on the clock / framerate changes + made by the frequency control, sick. */ + if (sd->freq == 1) { + start_cropx = 277; + start_cropy = 37; + } else { + start_cropx = 105; + start_cropy = 37; + } + } else { + start_cropx = 320; + start_cropy = 35; + } + + /* Work around to avoid FP arithmetics */ + #define SC(x) ((x) << 10) + + /* Scaling factors */ + fw = SC(sd->gspca_dev.width) / max_width; + fh = SC(sd->gspca_dev.height) / max_height; + + cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width)/fh; + ch = (fw >= fh) ? SC(sd->gspca_dev.height)/fw : max_height; + + sd->sensor_width = max_width; + sd->sensor_height = max_height; + + x = (max_width - cw) / 2; + y = (max_height - ch) / 2; + + ret += reg_w(sd, 0x10, start_cropx + x); + ret += reg_w(sd, 0x11, start_cropy + y); + ret += reg_w(sd, 0x12, start_cropx + x + cw); + ret += reg_w(sd, 0x13, start_cropy + y + ch); + + return ret; +} + +static int w9968cf_mode_init_regs(struct sd *sd) +{ + int ret = 0, val, vs_polarity, hs_polarity; + + ret += w9968cf_set_crop_window(sd); + + ret += reg_w(sd, 0x14, sd->gspca_dev.width); + ret += reg_w(sd, 0x15, sd->gspca_dev.height); + + /* JPEG width & height */ + ret += reg_w(sd, 0x30, sd->gspca_dev.width); + ret += reg_w(sd, 0x31, sd->gspca_dev.height); + + /* Y & UV frame buffer strides (in WORD) */ + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + ret += reg_w(sd, 0x2c, sd->gspca_dev.width/2); + ret += reg_w(sd, 0x2d, sd->gspca_dev.width/4); + } else + ret += reg_w(sd, 0x2c, sd->gspca_dev.width); + + ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */ + ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */ + + /* Transfer size in WORDS (for UYVY format only) */ + val = sd->gspca_dev.width * sd->gspca_dev.height; + ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */ + ret += reg_w(sd, 0x3e, val >> 16); /* high bits */ + + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + /* We may get called multiple times (usb isoc bw negotiat.) */ + if (!sd->jpeg_hdr) + sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL); + if (!sd->jpeg_hdr) + return -ENOMEM; + + jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height, + sd->gspca_dev.width, 0x22); /* JPEG 420 */ + jpeg_set_qual(sd->jpeg_hdr, sd->quality); + ret += w9968cf_upload_quantizationtables(sd); + } + + /* Video Capture Control Register */ + if (sd->sensor == SEN_OV7620) { + /* Seems to work around a bug in the image sensor */ + vs_polarity = 1; + hs_polarity = 1; + } else { + vs_polarity = 1; + hs_polarity = 0; + } + + val = (vs_polarity << 12) | (hs_polarity << 11); + + /* NOTE: We may not have enough memory to do double buffering while + doing compression (amount of memory differs per model cam). + So we use the second image buffer also as jpeg stream buffer + (see w9968cf_init), and disable double buffering. */ + if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + /* val |= 0x0002; YUV422P */ + val |= 0x0003; /* YUV420P */ + } else + val |= 0x0080; /* Enable HW double buffering */ + + /* val |= 0x0020; enable clamping */ + /* val |= 0x0008; enable (1-2-1) filter */ + /* val |= 0x000c; enable (2-3-6-3-2) filter */ + + val |= 0x8000; /* capt. enable */ + + ret += reg_w(sd, 0x16, val); + + sd->gspca_dev.empty_packet = 0; + + return ret; +} + +static void w9968cf_stop0(struct sd *sd) +{ + if (sd->gspca_dev.present) { + reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */ + reg_w(sd, 0x16, 0x0000); /* stop video capture */ + } + + kfree(sd->jpeg_hdr); + sd->jpeg_hdr = NULL; +} + +/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF + for the next frame). This seems to simply not be true when operating + in JPEG mode, in this case there may be empty packets within the + frame. So in JPEG mode use the JPEG SOI marker to detect SOF. + + Note to make things even more interesting the w9968cf sends *PLANAR* jpeg, + to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS, + V-data, EOI. */ +static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev, + u8 *data, /* isoc packet */ + int len) /* iso packet length */ +{ + struct sd *sd = (struct sd *) gspca_dev; + + if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat == + V4L2_PIX_FMT_JPEG) { + if (len >= 2 && + data[0] == 0xff && + data[1] == 0xd8) { + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + sd->jpeg_hdr, JPEG_HDR_SZ); + /* Strip the ff d8, our own header (which adds + huffman and quantization tables) already has this */ + len -= 2; + data += 2; + } + } else { + /* In UYVY mode an empty packet signals EOF */ + if (gspca_dev->empty_packet) { + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); + gspca_frame_add(gspca_dev, FIRST_PACKET, + NULL, 0); + gspca_dev->empty_packet = 0; + } + } + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); +} diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 49c3c1226e0e9..69e5dc4fc9def 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -61,17 +61,18 @@ struct sd { #define SENSOR_HV7131C 6 #define SENSOR_ICM105A 7 #define SENSOR_MC501CB 8 -#define SENSOR_OV7620 9 -/*#define SENSOR_OV7648 9 - same values */ -#define SENSOR_OV7630C 10 -#define SENSOR_PAS106 11 -#define SENSOR_PAS202B 12 -#define SENSOR_PB0330 13 -#define SENSOR_PO2030 14 -#define SENSOR_TAS5130CK 15 -#define SENSOR_TAS5130CXX 16 -#define SENSOR_TAS5130C_VF0250 17 -#define SENSOR_MAX 18 +#define SENSOR_MI0360SOC 9 +#define SENSOR_OV7620 10 +/*#define SENSOR_OV7648 10 - same values */ +#define SENSOR_OV7630C 11 +#define SENSOR_PAS106 12 +#define SENSOR_PAS202B 13 +#define SENSOR_PB0330 14 /* (MI0360) */ +#define SENSOR_PO2030 15 +#define SENSOR_TAS5130CK 16 +#define SENSOR_TAS5130CXX 17 +#define SENSOR_TAS5130C_VF0250 18 +#define SENSOR_MAX 19 unsigned short chip_revision; u8 *jpeg_hdr; @@ -420,9 +421,7 @@ static const struct usb_action adcm2700_NoFliker[] = { {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */ {} }; -static const struct usb_action cs2102_Initial[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action cs2102_Initial[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -471,88 +470,10 @@ static const struct usb_action cs2102_Initial[] = { {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x68, ZC3XX_R18D_YTARGET}, {0xa0, 0x00, 0x01ad}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */ - {0xa0, 0x44, ZC3XX_R121_GAMMA01}, - {0xa0, 0x64, ZC3XX_R122_GAMMA02}, - {0xa0, 0x84, ZC3XX_R123_GAMMA03}, - {0xa0, 0x9d, ZC3XX_R124_GAMMA04}, - {0xa0, 0xb2, ZC3XX_R125_GAMMA05}, - {0xa0, 0xc4, ZC3XX_R126_GAMMA06}, - {0xa0, 0xd3, ZC3XX_R127_GAMMA07}, - {0xa0, 0xe0, ZC3XX_R128_GAMMA08}, - {0xa0, 0xeb, ZC3XX_R129_GAMMA09}, - {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x18, ZC3XX_R130_GAMMA10}, - {0xa0, 0x20, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0e, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x23, 0x0001}, - {0xaa, 0x24, 0x0055}, - {0xaa, 0x25, 0x00cc}, - {0xaa, 0x21, 0x003f}, - {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, {} }; -static const struct usb_action cs2102_InitialScale[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action cs2102_InitialScale[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT}, @@ -601,57 +522,75 @@ static const struct usb_action cs2102_InitialScale[] = { {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x68, ZC3XX_R18D_YTARGET}, {0xa0, 0x00, 0x01ad}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */ - {0xa0, 0x44, ZC3XX_R121_GAMMA01}, - {0xa0, 0x64, ZC3XX_R122_GAMMA02}, - {0xa0, 0x84, ZC3XX_R123_GAMMA03}, - {0xa0, 0x9d, ZC3XX_R124_GAMMA04}, - {0xa0, 0xb2, ZC3XX_R125_GAMMA05}, - {0xa0, 0xc4, ZC3XX_R126_GAMMA06}, - {0xa0, 0xd3, ZC3XX_R127_GAMMA07}, - {0xa0, 0xe0, ZC3XX_R128_GAMMA08}, - {0xa0, 0xeb, ZC3XX_R129_GAMMA09}, - {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xff, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xff, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xff, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x18, ZC3XX_R130_GAMMA10}, - {0xa0, 0x20, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0e, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x00, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x00, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x00, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x01, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf4, ZC3XX_R10B_RGB01}, - {0xa0, 0xf4, ZC3XX_R10C_RGB02}, - {0xa0, 0xf4, ZC3XX_R10D_RGB10}, - {0xa0, 0x58, ZC3XX_R10E_RGB11}, - {0xa0, 0xf4, ZC3XX_R10F_RGB12}, - {0xa0, 0xf4, ZC3XX_R110_RGB20}, - {0xa0, 0xf4, ZC3XX_R111_RGB21}, - {0xa0, 0x58, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action cs2102_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x005f}, + {0xaa, 0x25, 0x0090}, + {0xaa, 0x21, 0x00dd}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_50HZScale[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00af}, + {0xaa, 0x25, 0x00c8}, + {0xaa, 0x21, 0x0068}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_60HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x0055}, + {0xaa, 0x25, 0x00cc}, + {0xaa, 0x21, 0x003f}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x39, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x70, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action cs2102_60HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, {0xaa, 0x23, 0x0000}, {0xaa, 0x24, 0x00aa}, @@ -671,162 +610,50 @@ static const struct usb_action cs2102_InitialScale[] = { {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1}, {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x40, ZC3XX_R116_RGAIN}, - {0xa0, 0x40, ZC3XX_R117_GGAIN}, - {0xa0, 0x40, ZC3XX_R118_BGAIN}, - {} -}; -static const struct usb_action cs2102_50HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x008c}, /* 00,0f,8c,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00ac}, /* 00,04,ac,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00ac}, /* 00,11,ac,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00ac}, /* 00,1d,ac,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x42, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,42,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x8c, ZC3XX_R01D_HSYNC_0}, /* 00,1d,8c,cc */ - {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */ - {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */ - {} -}; -static const struct usb_action cs2102_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0093}, /* 00,0f,93,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00a1}, /* 00,04,a1,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00a1}, /* 00,11,a1,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00a1}, /* 00,1d,a1,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf7, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f7,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x93, ZC3XX_R01D_HSYNC_0}, /* 00,1d,93,cc */ - {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */ - {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */ - {} -}; -static const struct usb_action cs2102_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x005d}, /* 00,0f,5d,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00aa}, /* 00,04,aa,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00aa}, /* 00,11,aa,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00aa}, /* 00,1d,aa,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xe4, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e4,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0x5d, ZC3XX_R01D_HSYNC_0}, /* 00,1d,5d,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */ - {} -}; -static const struct usb_action cs2102_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x00b7}, /* 00,0f,b7,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x00be}, /* 00,04,be,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x00be}, /* 00,11,be,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x00be}, /* 00,1d,be,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xfc, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,fc,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x69, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,69,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */ - {0xa0, 0xb7, ZC3XX_R01D_HSYNC_0}, /* 00,1d,b7,cc */ - {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ - {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */ {} }; static const struct usb_action cs2102_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0001}, + {0xaa, 0x24, 0x005f}, + {0xaa, 0x25, 0x0000}, + {0xaa, 0x21, 0x0001}, + {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action cs2102_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */ - {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */ - {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */ - {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */ - {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */ - {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */ - {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */ - {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xaa, 0x23, 0x0000}, + {0xaa, 0x24, 0x00af}, + {0xaa, 0x25, 0x0080}, + {0xaa, 0x21, 0x0001}, + {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x01, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {} }; @@ -4409,170 +4236,80 @@ static const struct usb_action pas202b_NoFlikerScale[] = { {} }; -static const struct usb_action pb03303x_Initial[] = { +/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */ +static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */ + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/ +/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, {0xaa, 0x01, 0x0001}, {0xaa, 0x06, 0x0000}, {0xaa, 0x08, 0x0483}, {0xaa, 0x01, 0x0004}, {0xaa, 0x08, 0x0006}, {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, + {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/ + {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x35, 0x0050}, + {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/ + {0xaa, 0x35, 0x507f}, /*jfm: was 0050*/ {0xaa, 0x30, 0x0005}, {0xaa, 0x31, 0x0000}, {0xaa, 0x58, 0x0078}, {0xaa, 0x62, 0x0411}, {0xaa, 0x2b, 0x0028}, - {0xaa, 0x2c, 0x0030}, - {0xaa, 0x2d, 0x0030}, - {0xaa, 0x2e, 0x0028}, + {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/ + {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/ + {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /* jfm: was 78 */ {0xa0, 0x61, ZC3XX_R116_RGAIN}, {0xa0, 0x65, ZC3XX_R118_BGAIN}, - - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x0d, 0x003a}, - {0xa0, 0x02, 0x003b}, - {0xa0, 0x00, 0x0038}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x0134}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; - -static const struct usb_action pb03303x_InitialScale[] = { +static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW}, - {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */ + {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, - {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, + {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/ +/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, + {0xdd, 0x00, 0x0200}, {0xaa, 0x01, 0x0001}, {0xaa, 0x06, 0x0000}, {0xaa, 0x08, 0x0483}, @@ -4582,111 +4319,111 @@ static const struct usb_action pb03303x_InitialScale[] = { {0xaa, 0x03, 0x01e7}, {0xaa, 0x04, 0x0287}, {0xaa, 0x07, 0x3002}, - {0xaa, 0x20, 0x1100}, - {0xaa, 0x35, 0x0050}, + {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/ + {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/ {0xaa, 0x30, 0x0005}, {0xaa, 0x31, 0x0000}, {0xaa, 0x58, 0x0078}, {0xaa, 0x62, 0x0411}, - {0xaa, 0x2b, 0x0028}, - {0xaa, 0x2c, 0x0030}, - {0xaa, 0x2d, 0x0030}, - {0xaa, 0x2e, 0x0028}, + {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/ + {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/ + {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/ + {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, - {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, + {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x78, ZC3XX_R18D_YTARGET}, + {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /*jfm: was 78*/ {0xa0, 0x61, ZC3XX_R116_RGAIN}, {0xa0, 0x65, ZC3XX_R118_BGAIN}, - - {0xa1, 0x01, 0x0002}, - - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - - {0xa0, 0x0d, 0x003a}, - {0xa0, 0x02, 0x003b}, - {0xa0, 0x00, 0x0038}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */ - {0xa0, 0x38, ZC3XX_R121_GAMMA01}, - {0xa0, 0x59, ZC3XX_R122_GAMMA02}, - {0xa0, 0x79, ZC3XX_R123_GAMMA03}, - {0xa0, 0x92, ZC3XX_R124_GAMMA04}, - {0xa0, 0xa7, ZC3XX_R125_GAMMA05}, - {0xa0, 0xb9, ZC3XX_R126_GAMMA06}, - {0xa0, 0xc8, ZC3XX_R127_GAMMA07}, - {0xa0, 0xd4, ZC3XX_R128_GAMMA08}, - {0xa0, 0xdf, ZC3XX_R129_GAMMA09}, - {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A}, - {0xa0, 0xee, ZC3XX_R12B_GAMMA0B}, - {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C}, - {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D}, - {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E}, - {0xa0, 0xff, ZC3XX_R12F_GAMMA0F}, - {0xa0, 0x26, ZC3XX_R130_GAMMA10}, - {0xa0, 0x22, ZC3XX_R131_GAMMA11}, - {0xa0, 0x20, ZC3XX_R132_GAMMA12}, - {0xa0, 0x1c, ZC3XX_R133_GAMMA13}, - {0xa0, 0x16, ZC3XX_R134_GAMMA14}, - {0xa0, 0x13, ZC3XX_R135_GAMMA15}, - {0xa0, 0x10, ZC3XX_R136_GAMMA16}, - {0xa0, 0x0d, ZC3XX_R137_GAMMA17}, - {0xa0, 0x0b, ZC3XX_R138_GAMMA18}, - {0xa0, 0x09, ZC3XX_R139_GAMMA19}, - {0xa0, 0x07, ZC3XX_R13A_GAMMA1A}, - {0xa0, 0x06, ZC3XX_R13B_GAMMA1B}, - {0xa0, 0x05, ZC3XX_R13C_GAMMA1C}, - {0xa0, 0x04, ZC3XX_R13D_GAMMA1D}, - {0xa0, 0x03, ZC3XX_R13E_GAMMA1E}, - {0xa0, 0x02, ZC3XX_R13F_GAMMA1F}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x0180}, + {} +}; +static const struct usb_action mi360soc_AE50HZ[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0562}, + {0xbb, 0x01, 0x09aa}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE50HZScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0934}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE60HZ[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x053d}, + {0xbb, 0x01, 0x096e}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x62, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; +static const struct usb_action mi360soc_AE60HZScale[] = { {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0009}, - {0xaa, 0x09, 0x0134}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0983}, {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, @@ -4696,20 +4433,60 @@ static const struct usb_action pb03303x_InitialScale[] = { {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, + {} +}; +static const struct usb_action mi360soc_AENoFliker[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x01, 0x0960}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action pb0330xx_Initial[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action mi360soc_AENoFlikerScale[] = { + {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0534}, + {0xbb, 0x02, 0x0960}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x34, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, + {} +}; + +static const struct usb_action pb0330_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, + {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, @@ -4721,11 +4498,12 @@ static const struct usb_action pb0330xx_Initial[] = { {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xdd, 0x00, 0x0200}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x0006}, {0xaa, 0x02, 0x0011}, - {0xaa, 0x03, 0x01e7}, - {0xaa, 0x04, 0x0287}, + {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/ + {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/ {0xaa, 0x06, 0x0003}, {0xaa, 0x07, 0x3002}, {0xaa, 0x20, 0x1100}, @@ -4743,88 +4521,21 @@ static const struct usb_action pb0330xx_Initial[] = { {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, + {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */ + {0xa0, 0x15, 0x01ae}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0066}, - {0xaa, 0x09, 0x02b2}, - {0xaa, 0x10, 0x0002}, - - {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0007}, -/* {0xa0, 0x30, 0x0007}, */ -/* {0xa0, 0x00, 0x0007}, */ + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ {} }; - -static const struct usb_action pb0330xx_InitialScale[] = { - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0008}, +static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, - {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 10 */ + {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, @@ -4836,6 +4547,7 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, + {0xdd, 0x00, 0x0200}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, {0xaa, 0x01, 0x0006}, {0xaa, 0x02, 0x0011}, @@ -4856,55 +4568,45 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID}, {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, - {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, - {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x00, 0x01ad}, - {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, - {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, - {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, - {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, - {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0002}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT}, - {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND}, - {0xa1, 0x01, 0x0091}, - {0xa1, 0x01, 0x0095}, - {0xa1, 0x01, 0x0096}, - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xf8, ZC3XX_R10B_RGB01}, - {0xa0, 0xf8, ZC3XX_R10C_RGB02}, - {0xa0, 0xf8, ZC3XX_R10D_RGB10}, - {0xa0, 0x50, ZC3XX_R10E_RGB11}, - {0xa0, 0xf8, ZC3XX_R10F_RGB12}, - {0xa0, 0xf8, ZC3XX_R110_RGB20}, - {0xa0, 0xf8, ZC3XX_R111_RGB21}, - {0xa0, 0x50, ZC3XX_R112_RGB22}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, + {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, + {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, + {0xa0, 0x09, 0x01ad}, + {0xa0, 0x15, 0x01ae}, + {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, + {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, + {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, + {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, + {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/ + {} +}; +static const struct usb_action pb0330_50HZ[] = { + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x055c}, + {0xbb, 0x01, 0x09aa}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, + {} +}; +static const struct usb_action pb0330_50HZScale[] = { {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0x05, 0x0066}, - {0xaa, 0x09, 0x02b2}, - {0xaa, 0x10, 0x0002}, + {0xbb, 0x00, 0x0566}, + {0xbb, 0x02, 0x09b2}, + {0xbb, 0x00, 0x1002}, {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, @@ -4912,124 +4614,102 @@ static const struct usb_action pb0330xx_InitialScale[] = { {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0}, {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x09, 0x01ad}, - {0xa0, 0x15, 0x01ae}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0008}, - {0xa1, 0x01, 0x0007}, -/* {0xa0, 0x30, 0x0007}, */ -/* {0xa0, 0x00, 0x0007}, */ - {} -}; -static const struct usb_action pb0330_50HZ[] = { - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,ee,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, /* 00,1d,68,cc */ - {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */ - {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */ - {} -}; -static const struct usb_action pb0330_50HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0xe5, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e5,cc */ - {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f0,cc */ - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ {} }; static const struct usb_action pb0330_60HZ[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,dd,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x43, ZC3XX_R01D_HSYNC_0}, /* 00,1d,43,cc */ - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x01, 0x0974}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_60HZScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ - {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */ - {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x02, 0x096c}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xd0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_NoFliker[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */ - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0509}, + {0xbb, 0x02, 0x0940}, + {0xbb, 0x00, 0x1002}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, {} }; static const struct usb_action pb0330_NoFlikerScale[] = { - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */ - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ - {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ - {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */ - {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */ - {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */ + {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, + {0xbb, 0x00, 0x0535}, + {0xbb, 0x01, 0x0980}, + {0xbb, 0x00, 0x1001}, + {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN}, + {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, + {0xa0, 0x35, ZC3XX_R01D_HSYNC_0}, + {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, + {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, + {0xa0, 0xe0, ZC3XX_R020_HSYNC_3}, {} }; @@ -5655,7 +5335,7 @@ static const struct usb_action tas5130CK_InitialScale[] = { {} }; -static const struct usb_action tas5130cxx_Initial[] = { +static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, @@ -5684,74 +5364,19 @@ static const struct usb_action tas5130cxx_Initial[] = { {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x95, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xec, ZC3XX_R10B_RGB01}, - {0xa0, 0xec, ZC3XX_R10C_RGB02}, - {0xa0, 0xec, ZC3XX_R10D_RGB10}, - {0xa0, 0x68, ZC3XX_R10E_RGB11}, - {0xa0, 0xec, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0xec, ZC3XX_R111_RGB21}, - {0xa0, 0x68, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x018d}, - {0xa0, 0x90, ZC3XX_R18D_YTARGET}, /* 90 */ - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - - {0xaa, 0xa3, 0x0001}, - {0xaa, 0xa4, 0x0077}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, - - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 00 */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 03 */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* e8 */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 0 */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 0 */ - {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 7d */ - - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 08 */ - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 24 */ - {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, - {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 50 */ - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; -static const struct usb_action tas5130cxx_InitialScale[] = { -/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */ +static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa1, 0x01, 0x0008}, - {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT}, {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING}, @@ -5775,63 +5400,13 @@ static const struct usb_action tas5130cxx_InitialScale[] = { {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION}, {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, - {0xa0, 0x68, ZC3XX_R18D_YTARGET}, - {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN}, + {0xa0, 0x95, ZC3XX_R18D_YTARGET}, + {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, {0xa0, 0x00, 0x01ad}, {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, - {0xa1, 0x01, 0x0002}, - {0xa1, 0x01, 0x0008}, - - {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, - {0xa1, 0x01, 0x0008}, /* clock ? */ - {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */ - {0xa1, 0x01, 0x01c8}, - {0xa1, 0x01, 0x01c9}, - {0xa1, 0x01, 0x01ca}, - {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */ - - {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */ - {0xa0, 0xec, ZC3XX_R10B_RGB01}, - {0xa0, 0xec, ZC3XX_R10C_RGB02}, - {0xa0, 0xec, ZC3XX_R10D_RGB10}, - {0xa0, 0x68, ZC3XX_R10E_RGB11}, - {0xa0, 0xec, ZC3XX_R10F_RGB12}, - {0xa0, 0xec, ZC3XX_R110_RGB20}, - {0xa0, 0xec, ZC3XX_R111_RGB21}, - {0xa0, 0x68, ZC3XX_R112_RGB22}, - - {0xa1, 0x01, 0x018d}, - {0xa0, 0x90, ZC3XX_R18D_YTARGET}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, - {0xaa, 0xa3, 0x0001}, - {0xaa, 0xa4, 0x0063}, - {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, - {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, - {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, - {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, - {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, - {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, - {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, - {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, - {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, - {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, - {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, - {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, - {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, - {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, - {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, - {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, - {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE}, - {0xa1, 0x01, 0x0180}, - {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE}, {} }; static const struct usb_action tas5130cxx_50HZ[] = { @@ -5841,20 +5416,22 @@ static const struct usb_action tas5130cxx_50HZ[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */ - {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,38,cc */ + {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */ {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_50HZScale[] = { @@ -5864,20 +5441,22 @@ static const struct usb_action tas5130cxx_50HZScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */ + {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ - {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_60HZ[] = { @@ -5887,20 +5466,22 @@ static const struct usb_action tas5130cxx_60HZ[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_60HZScale[] = { @@ -5910,20 +5491,22 @@ static const struct usb_action tas5130cxx_60HZScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */ + {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW}, {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */ - {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ - {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */ - {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */ + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, + {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, + {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */ + {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; static const struct usb_action tas5130cxx_NoFliker[] = { @@ -5933,13 +5516,13 @@ static const struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ @@ -5947,6 +5530,8 @@ static const struct usb_action tas5130cxx_NoFliker[] = { {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; @@ -5957,13 +5542,13 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = { {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */ {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */ - {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */ - {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */ - {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */ - {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */ - {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */ - {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */ - {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */ + {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID}, + {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW}, + {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, + {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, + {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW}, + {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE}, + {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE}, {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */ @@ -5971,6 +5556,8 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = { {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */ + {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW}, + {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, {} }; @@ -6303,8 +5890,10 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, reg, 0x0092); reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */ - msleep(25); + msleep(20); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + if (retbyte != 0x00) + err("i2c_r status error %02x", retbyte); retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */ retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */ PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)", @@ -6323,8 +5912,10 @@ static __u8 i2c_write(struct gspca_dev *gspca_dev, reg_w_i(gspca_dev->dev, valL, 0x93); reg_w_i(gspca_dev->dev, valH, 0x94); reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */ - msleep(15); + msleep(1); retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */ + if (retbyte != 0x00) + err("i2c_w status error %02x", retbyte); PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)", reg, valH, valL, retbyte); return retbyte; @@ -6359,7 +5950,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev, break; } action++; -/* msleep(1); */ + msleep(1); } } @@ -6380,11 +5971,13 @@ static void setmatrix(struct gspca_dev *gspca_dev) {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f}; static const __u8 po2030_matrix[9] = {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60}; + static const u8 tas5130c_matrix[9] = + {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68}; static const __u8 vf0250_matrix[9] = {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b}; static const __u8 *matrix_tb[SENSOR_MAX] = { adcm2700_matrix, /* SENSOR_ADCM2700 0 */ - NULL, /* SENSOR_CS2102 1 */ + ov7620_matrix, /* SENSOR_CS2102 1 */ NULL, /* SENSOR_CS2102K 2 */ gc0305_matrix, /* SENSOR_GC0305 3 */ NULL, /* SENSOR_HDCS2020b 4 */ @@ -6392,15 +5985,16 @@ static void setmatrix(struct gspca_dev *gspca_dev) NULL, /* SENSOR_HV7131C 6 */ NULL, /* SENSOR_ICM105A 7 */ NULL, /* SENSOR_MC501CB 8 */ - ov7620_matrix, /* SENSOR_OV7620 9 */ - NULL, /* SENSOR_OV7630C 10 */ - NULL, /* SENSOR_PAS106 11 */ - pas202b_matrix, /* SENSOR_PAS202B 12 */ - NULL, /* SENSOR_PB0330 13 */ - po2030_matrix, /* SENSOR_PO2030 14 */ - NULL, /* SENSOR_TAS5130CK 15 */ - NULL, /* SENSOR_TAS5130CXX 16 */ - vf0250_matrix, /* SENSOR_TAS5130C_VF0250 17 */ + gc0305_matrix, /* SENSOR_MI0360SOC 9 */ + ov7620_matrix, /* SENSOR_OV7620 10 */ + NULL, /* SENSOR_OV7630C 11 */ + NULL, /* SENSOR_PAS106 12 */ + pas202b_matrix, /* SENSOR_PAS202B 13 */ + gc0305_matrix, /* SENSOR_PB0330 14 */ + po2030_matrix, /* SENSOR_PO2030 15 */ + NULL, /* SENSOR_TAS5130CK 16 */ + tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */ + vf0250_matrix, /* SENSOR_TAS5130C_VF0250 18 */ }; matrix = matrix_tb[sd->sensor]; @@ -6640,39 +6234,43 @@ static int setlightfreq(struct gspca_dev *gspca_dev) {MC501CB_NoFliker, MC501CB_NoFlikerScale, MC501CB_50HZ, MC501CB_50HZScale, MC501CB_60HZ, MC501CB_60HZScale}, -/* SENSOR_OV7620 9 */ +/* SENSOR_MI0360SOC 9 */ + {mi360soc_AENoFlikerScale, mi360soc_AENoFliker, + mi360soc_AE50HZScale, mi360soc_AE50HZ, + mi360soc_AE60HZScale, mi360soc_AE60HZ}, +/* SENSOR_OV7620 10 */ {OV7620_NoFliker, OV7620_NoFliker, OV7620_50HZ, OV7620_50HZ, OV7620_60HZ, OV7620_60HZ}, -/* SENSOR_OV7630C 10 */ +/* SENSOR_OV7630C 11 */ {NULL, NULL, NULL, NULL, NULL, NULL}, -/* SENSOR_PAS106 11 */ +/* SENSOR_PAS106 12 */ {pas106b_NoFliker, pas106b_NoFliker, pas106b_50HZ, pas106b_50HZ, pas106b_60HZ, pas106b_60HZ}, -/* SENSOR_PAS202B 12 */ +/* SENSOR_PAS202B 13 */ {pas202b_NoFlikerScale, pas202b_NoFliker, pas202b_50HZScale, pas202b_50HZ, pas202b_60HZScale, pas202b_60HZ}, -/* SENSOR_PB0330 13 */ - {pb0330_NoFliker, pb0330_NoFlikerScale, - pb0330_50HZ, pb0330_50HZScale, - pb0330_60HZ, pb0330_60HZScale}, -/* SENSOR_PO2030 14 */ +/* SENSOR_PB0330 14 */ + {pb0330_NoFlikerScale, pb0330_NoFliker, + pb0330_50HZScale, pb0330_50HZ, + pb0330_60HZScale, pb0330_60HZ}, +/* SENSOR_PO2030 15 */ {PO2030_NoFliker, PO2030_NoFliker, PO2030_50HZ, PO2030_50HZ, PO2030_60HZ, PO2030_60HZ}, -/* SENSOR_TAS5130CK 15 */ - {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, - tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130CXX 16 */ - {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale, - tas5130cxx_50HZ, tas5130cxx_50HZScale, - tas5130cxx_60HZ, tas5130cxx_60HZScale}, -/* SENSOR_TAS5130C_VF0250 17 */ +/* SENSOR_TAS5130CK 16 */ + {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker, + tas5130cxx_50HZScale, tas5130cxx_50HZ, + tas5130cxx_60HZScale, tas5130cxx_60HZ}, +/* SENSOR_TAS5130CXX 17 */ + {tas5130cxx_NoFlikerScale, tas5130cxx_NoFliker, + tas5130cxx_50HZScale, tas5130cxx_50HZ, + tas5130cxx_60HZScale, tas5130cxx_60HZ}, +/* SENSOR_TAS5130C_VF0250 18 */ {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale, tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale, tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale}, @@ -6729,6 +6327,7 @@ static void send_unknown(struct usb_device *dev, int sensor) case SENSOR_ADCM2700: case SENSOR_GC0305: case SENSOR_OV7620: + case SENSOR_MI0360SOC: case SENSOR_PB0330: case SENSOR_PO2030: reg_w(dev, 0x0d, 0x003a); @@ -6820,7 +6419,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev) start_2wr_probe(dev, 0x0e); /* PAS202BCB */ reg_w(dev, 0x08, 0x008d); i2c_write(gspca_dev, 0x03, 0xaa, 0x00); - msleep(500); + msleep(50); retword = i2c_read(gspca_dev, 0x03); if (retword != 0) return 0x0e; /* PAS202BCB */ @@ -6863,7 +6462,8 @@ struct sensor_by_chipset_revision { __u8 internal_sensor_id; }; static const struct sensor_by_chipset_revision chipset_revision_sensor[] = { - {0xc001, 0x13}, /* MI0360 */ + {0xc000, 0x12}, /* TAS5130C */ + {0xc001, 0x13}, /* MI0360SOC */ {0xe001, 0x13}, {0x8001, 0x13}, {0x8000, 0x14}, /* CS2102K */ @@ -6963,7 +6563,6 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev) reg_w(dev, 0x01, 0x0001); reg_w(dev, 0xee, 0x008b); reg_w(dev, 0x03, 0x0012); -/* msleep(150); */ reg_w(dev, 0x01, 0x0012); reg_w(dev, 0x05, 0x0012); retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */ @@ -7025,7 +6624,7 @@ static int sd_config(struct gspca_dev *gspca_dev, int vga = 1; /* 1: vga, 0: sif */ static const __u8 gamma[SENSOR_MAX] = { 4, /* SENSOR_ADCM2700 0 */ - 5, /* SENSOR_CS2102 1 */ + 4, /* SENSOR_CS2102 1 */ 5, /* SENSOR_CS2102K 2 */ 4, /* SENSOR_GC0305 3 */ 4, /* SENSOR_HDCS2020b 4 */ @@ -7033,15 +6632,16 @@ static int sd_config(struct gspca_dev *gspca_dev, 4, /* SENSOR_HV7131C 6 */ 4, /* SENSOR_ICM105A 7 */ 4, /* SENSOR_MC501CB 8 */ - 3, /* SENSOR_OV7620 9 */ - 4, /* SENSOR_OV7630C 10 */ - 4, /* SENSOR_PAS106 11 */ - 4, /* SENSOR_PAS202B 12 */ - 4, /* SENSOR_PB0330 13 */ - 4, /* SENSOR_PO2030 14 */ - 4, /* SENSOR_TAS5130CK 15 */ - 4, /* SENSOR_TAS5130CXX 16 */ - 3, /* SENSOR_TAS5130C_VF0250 17 */ + 4, /* SENSOR_MI0360SOC 9 */ + 3, /* SENSOR_OV7620 10 */ + 4, /* SENSOR_OV7630C 11 */ + 4, /* SENSOR_PAS106 12 */ + 4, /* SENSOR_PAS202B 13 */ + 4, /* SENSOR_PB0330 14 */ + 4, /* SENSOR_PO2030 15 */ + 4, /* SENSOR_TAS5130CK 16 */ + 3, /* SENSOR_TAS5130CXX 17 */ + 3, /* SENSOR_TAS5130C_VF0250 18 */ }; /* define some sensors from the vendor/product */ @@ -7103,7 +6703,7 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case 0x10: case 0x12: - PDEBUG(D_PROBE, "Find Sensor TAS5130"); + PDEBUG(D_PROBE, "Find Sensor TAS5130C"); sd->sensor = SENSOR_TAS5130CXX; break; case 0x11: @@ -7112,9 +6712,9 @@ static int sd_config(struct gspca_dev *gspca_dev, break; case 0x13: PDEBUG(D_PROBE, - "Find Sensor MI0360. Chip revision %x", + "Find Sensor MI0360SOC. Chip revision %x", sd->chip_revision); - sd->sensor = SENSOR_PB0330; + sd->sensor = SENSOR_MI0360SOC; break; case 0x14: PDEBUG(D_PROBE, @@ -7228,17 +6828,17 @@ static int sd_start(struct gspca_dev *gspca_dev) {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */ {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */ {MC501CB_InitialScale, MC501CB_Initial}, /* 8 */ - {OV7620_mode0, OV7620_mode1}, /* 9 */ - {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */ - {pas106b_InitialScale, pas106b_Initial}, /* 11 */ - {pas202b_Initial, pas202b_InitialScale}, /* 12 */ - {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */ -/* or {pb03303x_InitialScale, pb03303x_Initial}, */ - {PO2030_mode0, PO2030_mode1}, /* 14 */ - {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */ - {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */ + {mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */ + {OV7620_mode0, OV7620_mode1}, /* 10 */ + {ov7630c_InitialScale, ov7630c_Initial}, /* 11 */ + {pas106b_InitialScale, pas106b_Initial}, /* 12 */ + {pas202b_Initial, pas202b_InitialScale}, /* 13 */ + {pb0330_Initial, pb0330_InitialScale}, /* 14 */ + {PO2030_mode0, PO2030_mode1}, /* 15 */ + {tas5130CK_InitialScale, tas5130CK_Initial}, /* 16 */ + {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */ {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial}, - /* 17 */ + /* 18 */ }; /* create the JPEG header */ @@ -7258,19 +6858,6 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_PAS106: usb_exchange(gspca_dev, pas106b_Initial_com); break; - case SENSOR_PB0330: - if (mode) { - if (sd->chip_revision == 0xc001 - || sd->chip_revision == 0xe001 - || sd->chip_revision == 0x8001) - zc3_init = pb03303x_Initial; - } else { - if (sd->chip_revision == 0xc001 - || sd->chip_revision == 0xe001 - || sd->chip_revision == 0x8001) - zc3_init = pb03303x_InitialScale; - } - break; } usb_exchange(gspca_dev, zc3_init); @@ -7310,10 +6897,8 @@ static int sd_start(struct gspca_dev *gspca_dev) /* set the gamma tables when not set */ switch (sd->sensor) { - case SENSOR_CS2102: /* gamma set in xxx_Initial */ - case SENSOR_CS2102K: + case SENSOR_CS2102K: /* gamma set in xxx_Initial */ case SENSOR_HDCS2020b: - case SENSOR_PB0330: /* pb with chip_revision - see above */ case SENSOR_OV7630C: case SENSOR_TAS5130CK: break; @@ -7365,7 +6950,7 @@ static int sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); switch (sd->sensor) { case SENSOR_PO2030: - msleep(500); + msleep(50); reg_r(gspca_dev, 0x0008); reg_r(gspca_dev, 0x0007); /*fall thru*/ @@ -7389,17 +6974,16 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } static void sd_pkt_scan(struct gspca_dev *gspca_dev, - struct gspca_frame *frame, - __u8 *data, + u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */ - frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, - data, 0); + gspca_frame_add(gspca_dev, LAST_PACKET, + NULL, 0); /* put the JPEG header in the new frame */ - gspca_frame_add(gspca_dev, FIRST_PACKET, frame, + gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); /* remove the webcam's header: @@ -7411,7 +6995,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, data += 18; len -= 18; } - gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len); + gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val) diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 2eb9dc2ebe597..b5439cabb381e 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -139,7 +139,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n"); - goto exit; + goto exit_urb; } buf->urb = urb; @@ -148,7 +148,7 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) if (!mem) { v4l2_err(&dev->v4l2_dev, "cannot allocate usb transfer buffer\n"); - goto exit; + goto exit_urb_buffer; } usb_fill_bulk_urb(buf->urb, dev->udev, @@ -161,6 +161,10 @@ int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count) list_add_tail(&buf->buff_list, &dev->free_buff_list); } return 0; +exit_urb_buffer: + usb_free_urb(urb); +exit_urb: + kfree(buf); exit: hdpvr_free_buffers(dev); return retval; diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index 71c211402eb5e..60d992ee2589b 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -251,7 +251,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (input < 0 || input >= HEXIUM_INPUTS) + if (input >= HEXIUM_INPUTS) return -EINVAL; hexium->cur_input = input; diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 39d65ca41c627..938a1f8f880a0 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -350,7 +350,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct hexium *hexium = (struct hexium *) dev->ext_priv; - if (input < 0 || input >= HEXIUM_INPUTS) + if (input >= HEXIUM_INPUTS) return -EINVAL; hexium->cur_input = input; diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index 247d3115a9b7c..64360d26b32d0 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -275,7 +275,7 @@ static void ir_key_poll(struct IR_i2c *ir) if (0 == rc) { ir_input_nokey(ir->input, &ir->ir); } else { - ir_input_keydown(ir->input, &ir->ir, ir_key, ir_raw); + ir_input_keydown(ir->input, &ir->ir, ir_key); } } @@ -299,7 +299,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct ir_scancode_table *ir_codes = NULL; const char *name = NULL; - int ir_type; + int ir_type = 0; struct IR_i2c *ir; struct input_dev *input_dev; struct i2c_adapter *adap = client->adapter; @@ -353,10 +353,8 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_type = IR_TYPE_RC5; ir_codes = &ir_codes_fusionhdtv_mce_table; break; - case 0x7a: case 0x47: case 0x71: - case 0x2d: if (adap->id == I2C_HW_B_CX2388x || adap->id == I2C_HW_B_CX2341X) { /* Handled by cx88-input */ @@ -381,10 +379,6 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) ir_type = IR_TYPE_OTHER; ir_codes = &ir_codes_avermedia_cardbus_table; break; - default: - dprintk(1, DEVNAME ": Unsupported i2c address 0x%02x\n", addr); - err = -ENODEV; - goto err_out_free; } /* Let the caller override settings */ @@ -427,7 +421,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) } /* Make sure we are all setup before going on */ - if (!name || !ir->get_key || !ir_codes) { + if (!name || !ir->get_key || !ir_type || !ir_codes) { dprintk(1, DEVNAME ": Unsupported device at address 0x%02x\n", addr); err = -ENODEV; @@ -443,7 +437,10 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_name(&client->dev)); /* init + register input device */ - ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir->ir_codes); + if (err < 0) + goto err_out_free; + input_dev->id.bustype = BUS_I2C; input_dev->name = ir->name; input_dev->phys = ir->phys; @@ -462,6 +459,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) return 0; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -475,6 +473,7 @@ static int ir_remove(struct i2c_client *client) cancel_delayed_work_sync(&ir->work); /* unregister device */ + ir_input_free(ir->input); input_unregister_device(ir->input); /* free memory */ diff --git a/drivers/media/video/ivtv/ivtv-cards.c b/drivers/media/video/ivtv/ivtv-cards.c index 4873b6ca58010..79d0fe4990d66 100644 --- a/drivers/media/video/ivtv/ivtv-cards.c +++ b/drivers/media/video/ivtv/ivtv-cards.c @@ -136,7 +136,8 @@ static const struct ivtv_card ivtv_card_pvr350 = { .hw_audio = IVTV_HW_MSP34XX, .hw_audio_ctrl = IVTV_HW_MSP34XX, .hw_all = IVTV_HW_MSP34XX | IVTV_HW_SAA7115 | - IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + IVTV_HW_SAA7127 | IVTV_HW_TVEEPROM | IVTV_HW_TUNER | + IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, IVTV_SAA71XX_COMPOSITE4 }, { IVTV_CARD_INPUT_SVIDEO1, 1, IVTV_SAA71XX_SVIDEO0 }, @@ -199,7 +200,9 @@ static const struct ivtv_card ivtv_card_pvr150 = { .hw_audio_ctrl = IVTV_HW_CX25840, .hw_muxer = IVTV_HW_WM8775, .hw_all = IVTV_HW_WM8775 | IVTV_HW_CX25840 | - IVTV_HW_TVEEPROM | IVTV_HW_TUNER, + IVTV_HW_TVEEPROM | IVTV_HW_TUNER | + IVTV_HW_I2C_IR_RX_HAUP_EXT | IVTV_HW_I2C_IR_RX_HAUP_INT | + IVTV_HW_Z8F0811_IR_HAUP, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE7 }, { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO1 }, @@ -955,7 +958,8 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, .hw_audio_ctrl = IVTV_HW_CX25840, - .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739, + .hw_all = IVTV_HW_CX25840 | IVTV_HW_TUNER | IVTV_HW_WM8739 | + IVTV_HW_I2C_IR_RX_AVER, .video_inputs = { { IVTV_CARD_INPUT_VID_TUNER, 0, CX25840_COMPOSITE2 }, { IVTV_CARD_INPUT_SVIDEO1, 1, CX25840_SVIDEO3 }, @@ -965,6 +969,7 @@ static const struct ivtv_card ivtv_card_avertv_mce116 = { { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, { IVTV_CARD_INPUT_LINE_IN1, CX25840_AUDIO_SERIAL, 1 }, }, + .radio_input = { IVTV_CARD_INPUT_AUD_TUNER, CX25840_AUDIO5 }, /* enable line-in */ .gpio_init = { .direction = 0xe000, .initial_value = 0x4000 }, .xceive_pin = 10, @@ -1025,13 +1030,15 @@ static const struct ivtv_card ivtv_card_aver_pvr150 = { /* AVerMedia UltraTV 1500 MCE (newer non-cx88 version, M113 variant) card */ static const struct ivtv_card_pci_info ivtv_pci_aver_ultra1500mce[] = { - { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc019 }, /* NTSC */ + { PCI_DEVICE_ID_IVTV16, IVTV_PCI_ID_AVERMEDIA, 0xc01b }, /* PAL/SECAM */ { 0, 0, 0 } }; static const struct ivtv_card ivtv_card_aver_ultra1500mce = { .type = IVTV_CARD_AVER_ULTRA1500MCE, .name = "AVerMedia UltraTV 1500 MCE / AVerTV M113 Philips Tuner", + .comment = "For non-NTSC tuners, use the pal= or secam= module options", .v4l2_capabilities = IVTV_CAP_ENCODER, .hw_video = IVTV_HW_CX25840, .hw_audio = IVTV_HW_CX25840, @@ -1058,6 +1065,7 @@ static const struct ivtv_card ivtv_card_aver_ultra1500mce = { .tuners = { /* The UltraTV 1500 MCE has a Philips FM1236 MK5 TV/FM tuner */ { .std = V4L2_STD_MN, .tuner = TUNER_PHILIPS_FM1236_MK3 }, + { .std = V4L2_STD_PAL_SECAM, .tuner = TUNER_PHILIPS_FM1216MK5 }, }, .pci_list = ivtv_pci_aver_ultra1500mce, .i2c = &ivtv_i2c_std, diff --git a/drivers/media/video/ivtv/ivtv-cards.h b/drivers/media/video/ivtv/ivtv-cards.h index e99a0a2555782..6148827ec8854 100644 --- a/drivers/media/video/ivtv/ivtv-cards.h +++ b/drivers/media/video/ivtv/ivtv-cards.h @@ -87,26 +87,43 @@ #define IVTV_PCI_ID_GOTVIEW1 0xffac #define IVTV_PCI_ID_GOTVIEW2 0xffad -/* hardware flags, no gaps allowed, IVTV_HW_GPIO must always be last */ -#define IVTV_HW_CX25840 (1 << 0) -#define IVTV_HW_SAA7115 (1 << 1) -#define IVTV_HW_SAA7127 (1 << 2) -#define IVTV_HW_MSP34XX (1 << 3) -#define IVTV_HW_TUNER (1 << 4) -#define IVTV_HW_WM8775 (1 << 5) -#define IVTV_HW_CS53L32A (1 << 6) -#define IVTV_HW_TVEEPROM (1 << 7) -#define IVTV_HW_SAA7114 (1 << 8) -#define IVTV_HW_UPD64031A (1 << 9) -#define IVTV_HW_UPD6408X (1 << 10) -#define IVTV_HW_SAA717X (1 << 11) -#define IVTV_HW_WM8739 (1 << 12) -#define IVTV_HW_VP27SMPX (1 << 13) -#define IVTV_HW_M52790 (1 << 14) -#define IVTV_HW_GPIO (1 << 15) +/* hardware flags, no gaps allowed */ +#define IVTV_HW_CX25840 (1 << 0) +#define IVTV_HW_SAA7115 (1 << 1) +#define IVTV_HW_SAA7127 (1 << 2) +#define IVTV_HW_MSP34XX (1 << 3) +#define IVTV_HW_TUNER (1 << 4) +#define IVTV_HW_WM8775 (1 << 5) +#define IVTV_HW_CS53L32A (1 << 6) +#define IVTV_HW_TVEEPROM (1 << 7) +#define IVTV_HW_SAA7114 (1 << 8) +#define IVTV_HW_UPD64031A (1 << 9) +#define IVTV_HW_UPD6408X (1 << 10) +#define IVTV_HW_SAA717X (1 << 11) +#define IVTV_HW_WM8739 (1 << 12) +#define IVTV_HW_VP27SMPX (1 << 13) +#define IVTV_HW_M52790 (1 << 14) +#define IVTV_HW_GPIO (1 << 15) +#define IVTV_HW_I2C_IR_RX_AVER (1 << 16) +#define IVTV_HW_I2C_IR_RX_HAUP_EXT (1 << 17) /* External before internal */ +#define IVTV_HW_I2C_IR_RX_HAUP_INT (1 << 18) +#define IVTV_HW_Z8F0811_IR_TX_HAUP (1 << 19) +#define IVTV_HW_Z8F0811_IR_RX_HAUP (1 << 20) + +#define IVTV_HW_Z8F0811_IR_HAUP (IVTV_HW_Z8F0811_IR_RX_HAUP | \ + IVTV_HW_Z8F0811_IR_TX_HAUP) #define IVTV_HW_SAA711X (IVTV_HW_SAA7115 | IVTV_HW_SAA7114) +#define IVTV_HW_IR_RX_ANY (IVTV_HW_I2C_IR_RX_AVER | \ + IVTV_HW_I2C_IR_RX_HAUP_EXT | \ + IVTV_HW_I2C_IR_RX_HAUP_INT | \ + IVTV_HW_Z8F0811_IR_RX_HAUP) + +#define IVTV_HW_IR_TX_ANY (IVTV_HW_Z8F0811_IR_TX_HAUP) + +#define IVTV_HW_IR_ANY (IVTV_HW_IR_RX_ANY | IVTV_HW_IR_TX_ANY) + /* video inputs */ #define IVTV_CARD_INPUT_VID_TUNER 1 #define IVTV_CARD_INPUT_SVIDEO1 2 diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 7cdbc1a8f218b..347c3344f56d4 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -91,10 +91,15 @@ static int radio[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }; +static int i2c_clock_period[IVTV_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1 }; static unsigned int cardtype_c = 1; static unsigned int tuner_c = 1; static unsigned int radio_c = 1; +static unsigned int i2c_clock_period_c = 1; static char pal[] = "---"; static char secam[] = "--"; static char ntsc[] = "-"; @@ -151,6 +156,7 @@ module_param(dec_vbi_buffers, int, 0644); module_param(tunertype, int, 0644); module_param(newi2c, int, 0644); +module_param_array(i2c_clock_period, int, &i2c_clock_period_c, 0644); MODULE_PARM_DESC(tuner, "Tuner type selection,\n" "\t\t\tsee tuner.h for values"); @@ -245,6 +251,10 @@ MODULE_PARM_DESC(newi2c, "Use new I2C implementation\n" "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" "\t\t\tDefault is autodetect"); +MODULE_PARM_DESC(i2c_clock_period, + "Period of SCL for the I2C bus controlled by the CX23415/6\n" + "\t\t\tMin: 10 usec (100 kHz), Max: 4500 usec (222 Hz)\n" + "\t\t\tDefault: " __stringify(IVTV_DEFAULT_I2C_CLOCK_PERIOD)); MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card"); @@ -600,6 +610,15 @@ static void ivtv_process_options(struct ivtv *itv) itv->options.cardtype = cardtype[itv->instance]; itv->options.tuner = tuner[itv->instance]; itv->options.radio = radio[itv->instance]; + + itv->options.i2c_clock_period = i2c_clock_period[itv->instance]; + if (itv->options.i2c_clock_period == -1) + itv->options.i2c_clock_period = IVTV_DEFAULT_I2C_CLOCK_PERIOD; + else if (itv->options.i2c_clock_period < 10) + itv->options.i2c_clock_period = 10; + else if (itv->options.i2c_clock_period > 4500) + itv->options.i2c_clock_period = 4500; + itv->options.newi2c = newi2c; if (tunertype < -1 || tunertype > 1) { IVTV_WARN("Invalid tunertype argument, will autodetect instead\n"); @@ -865,6 +884,10 @@ static void ivtv_load_and_init_modules(struct ivtv *itv) itv->hw_flags |= device; } + /* probe for legacy IR controllers that aren't in card definitions */ + if ((itv->hw_flags & IVTV_HW_IR_ANY) == 0) + ivtv_i2c_new_ir_legacy(itv); + if (itv->card->hw_all & IVTV_HW_CX25840) itv->sd_video = ivtv_find_hw(itv, IVTV_HW_CX25840); else if (itv->card->hw_all & IVTV_HW_SAA717X) diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h index 440f7328a7eda..e4816da6482b6 100644 --- a/drivers/media/video/ivtv/ivtv-driver.h +++ b/drivers/media/video/ivtv/ivtv-driver.h @@ -64,6 +64,7 @@ #include #include #include +#include #include @@ -176,12 +177,16 @@ extern int ivtv_debug; #define IVTV_MAX_PGM_INDEX (400) +/* Default I2C SCL period in microseconds */ +#define IVTV_DEFAULT_I2C_CLOCK_PERIOD 20 + struct ivtv_options { int kilobytes[IVTV_MAX_STREAMS]; /* size in kilobytes of each stream */ int cardtype; /* force card type on load */ int tuner; /* set tuner on load */ int radio; /* enable/disable radio */ int newi2c; /* new I2C algorithm */ + int i2c_clock_period; /* period of SCL for I2C bus */ }; /* ivtv-specific mailbox template */ @@ -677,6 +682,7 @@ struct ivtv { int i2c_state; /* i2c bit state */ struct mutex i2c_bus_lock; /* lock i2c bus */ + struct IR_i2c_init_data ir_i2c_init_data; /* Program Index information */ u32 pgm_info_offset; /* start of pgm info in encoder memory */ diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c index b9c71e61f7d62..2ee03c2a1b587 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.c +++ b/drivers/media/video/ivtv/ivtv-i2c.c @@ -88,6 +88,11 @@ #define IVTV_UPD64083_I2C_ADDR 0x5c #define IVTV_VP27SMPX_I2C_ADDR 0x5b #define IVTV_M52790_I2C_ADDR 0x48 +#define IVTV_AVERMEDIA_IR_RX_I2C_ADDR 0x40 +#define IVTV_HAUP_EXT_IR_RX_I2C_ADDR 0x1a +#define IVTV_HAUP_INT_IR_RX_I2C_ADDR 0x18 +#define IVTV_Z8F0811_IR_TX_I2C_ADDR 0x70 +#define IVTV_Z8F0811_IR_RX_I2C_ADDR 0x71 /* This array should match the IVTV_HW_ defines */ static const u8 hw_addrs[] = { @@ -106,7 +111,12 @@ static const u8 hw_addrs[] = { IVTV_WM8739_I2C_ADDR, IVTV_VP27SMPX_I2C_ADDR, IVTV_M52790_I2C_ADDR, - 0 /* IVTV_HW_GPIO dummy driver ID */ + 0, /* IVTV_HW_GPIO dummy driver ID */ + IVTV_AVERMEDIA_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_AVER */ + IVTV_HAUP_EXT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + IVTV_HAUP_INT_IR_RX_I2C_ADDR, /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + IVTV_Z8F0811_IR_TX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + IVTV_Z8F0811_IR_RX_I2C_ADDR, /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the IVTV_HW_ defines */ @@ -126,7 +136,12 @@ static const char *hw_modules[] = { "wm8739", "vp27smpx", "m52790", - NULL + NULL, + NULL, /* IVTV_HW_I2C_IR_RX_AVER */ + NULL, /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + NULL, /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + NULL, /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + NULL, /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; /* This array should match the IVTV_HW_ defines */ @@ -147,8 +162,95 @@ static const char * const hw_devicenames[] = { "vp27smpx", "m52790", "gpio", + "ir_video", /* IVTV_HW_I2C_IR_RX_AVER */ + "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_EXT */ + "ir_video", /* IVTV_HW_I2C_IR_RX_HAUP_INT */ + "ir_tx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_TX_HAUP */ + "ir_rx_z8f0811_haup", /* IVTV_HW_Z8F0811_IR_RX_HAUP */ }; +static int ivtv_i2c_new_ir(struct ivtv *itv, u32 hw, const char *type, u8 addr) +{ + struct i2c_board_info info; + struct i2c_adapter *adap = &itv->i2c_adap; + struct IR_i2c_init_data *init_data = &itv->ir_i2c_init_data; + unsigned short addr_list[2] = { addr, I2C_CLIENT_END }; + + /* Only allow one IR transmitter to be registered per board */ + if (hw & IVTV_HW_IR_TX_ANY) { + if (itv->hw_flags & IVTV_HW_IR_TX_ANY) + return -1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, type, I2C_NAME_SIZE); + return i2c_new_probed_device(adap, &info, addr_list) == NULL + ? -1 : 0; + } + + /* Only allow one IR receiver to be registered per board */ + if (itv->hw_flags & IVTV_HW_IR_RX_ANY) + return -1; + + /* Our default information for ir-kbd-i2c.c to use */ + switch (hw) { + case IVTV_HW_I2C_IR_RX_AVER: + init_data->ir_codes = &ir_codes_avermedia_cardbus_table; + init_data->internal_get_key_func = + IR_KBD_GET_KEY_AVERMEDIA_CARDBUS; + init_data->type = IR_TYPE_OTHER; + init_data->name = "AVerMedia AVerTV card"; + break; + case IVTV_HW_I2C_IR_RX_HAUP_EXT: + case IVTV_HW_I2C_IR_RX_HAUP_INT: + /* Default to old black remote */ + init_data->ir_codes = &ir_codes_rc5_tv_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP; + init_data->type = IR_TYPE_RC5; + init_data->name = itv->card_name; + break; + case IVTV_HW_Z8F0811_IR_RX_HAUP: + /* Default to grey remote */ + init_data->ir_codes = &ir_codes_hauppauge_new_table; + init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR; + init_data->type = IR_TYPE_RC5; + init_data->name = itv->card_name; + break; + } + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.platform_data = init_data; + strlcpy(info.type, type, I2C_NAME_SIZE); + + return i2c_new_probed_device(adap, &info, addr_list) == NULL ? -1 : 0; +} + +/* Instantiate the IR receiver device using probing -- undesirable */ +struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv) +{ + struct i2c_board_info info; + /* + * The external IR receiver is at i2c address 0x34. + * The internal IR receiver is at i2c address 0x30. + * + * In theory, both can be fitted, and Hauppauge suggests an external + * overrides an internal. That's why we probe 0x1a (~0x34) first. CB + * + * Some of these addresses we probe may collide with other i2c address + * allocations, so this function must be called after all other i2c + * devices we care about are registered. + */ + const unsigned short addr_list[] = { + 0x1a, /* Hauppauge IR external - collides with WM8739 */ + 0x18, /* Hauppauge IR internal */ + 0x71, /* Hauppauge IR (PVR150) */ + 0x6b, /* Adaptec IR */ + I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + return i2c_new_probed_device(&itv->i2c_adap, &info, addr_list); +} + int ivtv_i2c_register(struct ivtv *itv, unsigned idx) { struct v4l2_subdev *sd; @@ -178,8 +280,15 @@ int ivtv_i2c_register(struct ivtv *itv, unsigned idx) sd->grp_id = 1 << idx; return sd ? 0 : -1; } + + if (hw & IVTV_HW_IR_ANY) + return ivtv_i2c_new_ir(itv, hw, type, hw_addrs[idx]); + + /* Is it not an I2C device or one we do not wish to register? */ if (!hw_addrs[idx]) return -1; + + /* It's an I2C device other than an analog tuner or IR chip */ if (hw == IVTV_HW_UPD64031A || hw == IVTV_HW_UPD6408X) { sd = v4l2_i2c_new_subdev(&itv->v4l2_dev, adap, mod, type, 0, I2C_ADDRS(hw_addrs[idx])); @@ -564,20 +673,22 @@ static struct i2c_adapter ivtv_i2c_adap_template = { .owner = THIS_MODULE, }; +#define IVTV_ALGO_BIT_TIMEOUT (2) /* seconds */ + static const struct i2c_algo_bit_data ivtv_i2c_algo_template = { .setsda = ivtv_setsda_old, .setscl = ivtv_setscl_old, .getsda = ivtv_getsda_old, .getscl = ivtv_getscl_old, - .udelay = 10, - .timeout = 200, + .udelay = IVTV_DEFAULT_I2C_CLOCK_PERIOD / 2, /* microseconds */ + .timeout = IVTV_ALGO_BIT_TIMEOUT * HZ, /* jiffies */ }; static struct i2c_client ivtv_i2c_client_template = { .name = "ivtv internal", }; -/* init + register i2c adapter + instantiate IR receiver */ +/* init + register i2c adapter */ int init_ivtv_i2c(struct ivtv *itv) { int retval; @@ -585,11 +696,10 @@ int init_ivtv_i2c(struct ivtv *itv) IVTV_DEBUG_I2C("i2c init\n"); /* Sanity checks for the I2C hardware arrays. They must be the - * same size and GPIO must be the last entry. + * same size. */ if (ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) || - ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules) || - IVTV_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 1))) { + ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_modules)) { IVTV_ERR("Mismatched I2C hardware arrays\n"); return -ENODEV; } @@ -602,6 +712,7 @@ int init_ivtv_i2c(struct ivtv *itv) memcpy(&itv->i2c_algo, &ivtv_i2c_algo_template, sizeof(struct i2c_algo_bit_data)); } + itv->i2c_algo.udelay = itv->options.i2c_clock_period / 2; itv->i2c_algo.data = itv; itv->i2c_adap.algo_data = &itv->i2c_algo; @@ -623,32 +734,6 @@ int init_ivtv_i2c(struct ivtv *itv) else retval = i2c_bit_add_bus(&itv->i2c_adap); - /* Instantiate the IR receiver device, if present */ - if (retval == 0) { - struct i2c_board_info info; - /* The external IR receiver is at i2c address 0x34 (0x35 for - reads). Future Hauppauge cards will have an internal - receiver at 0x30 (0x31 for reads). In theory, both can be - fitted, and Hauppauge suggest an external overrides an - internal. - - That's why we probe 0x1a (~0x34) first. CB - */ - const unsigned short addr_list[] = { - 0x1a, /* Hauppauge IR external */ - 0x18, /* Hauppauge IR internal */ - 0x71, /* Hauppauge IR (PVR150) */ - 0x64, /* Pixelview IR */ - 0x30, /* KNC ONE IR */ - 0x6b, /* Adaptec IR */ - I2C_CLIENT_END - }; - - memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - i2c_new_probed_device(&itv->i2c_adap, &info, addr_list); - } - return retval; } diff --git a/drivers/media/video/ivtv/ivtv-i2c.h b/drivers/media/video/ivtv/ivtv-i2c.h index 396928a06a54d..9332920ca4ff9 100644 --- a/drivers/media/video/ivtv/ivtv-i2c.h +++ b/drivers/media/video/ivtv/ivtv-i2c.h @@ -21,6 +21,7 @@ #ifndef IVTV_I2C_H #define IVTV_I2C_H +struct i2c_client *ivtv_i2c_new_ir_legacy(struct ivtv *itv); int ivtv_i2c_register(struct ivtv *itv, unsigned idx); struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw); diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 3454070e63f00..c1fc6dc776f58 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -478,7 +478,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) DEB_EE(("VIDIOC_S_INPUT %d.\n", input)); - if (input < 0 || input >= MXB_INPUTS) + if (input >= MXB_INPUTS) return -EINVAL; mxb->cur_input = input; diff --git a/drivers/media/video/ov9640.c b/drivers/media/video/ov9640.c new file mode 100644 index 0000000000000..c81ae21928874 --- /dev/null +++ b/drivers/media/video/ov9640.c @@ -0,0 +1,801 @@ +/* + * OmniVision OV96xx Camera Driver + * + * Copyright (C) 2009 Marek Vasut + * + * Based on ov772x camera driver: + * + * Copyright (C) 2008 Renesas Solutions Corp. + * Kuninori Morimoto + * + * Based on ov7670 and soc_camera_platform driver, + * + * Copyright 2006-7 Jonathan Corbet + * Copyright (C) 2008 Magnus Damm + * Copyright (C) 2008, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ov9640.h" + +/* default register setup */ +static const struct ov9640_reg ov9640_regs_dflt[] = { + { OV9640_COM5, OV9640_COM5_SYSCLK | OV9640_COM5_LONGEXP }, + { OV9640_COM6, OV9640_COM6_OPT_BLC | OV9640_COM6_ADBLC_BIAS | + OV9640_COM6_FMT_RST | OV9640_COM6_ADBLC_OPTEN }, + { OV9640_PSHFT, OV9640_PSHFT_VAL(0x01) }, + { OV9640_ACOM, OV9640_ACOM_2X_ANALOG | OV9640_ACOM_RSVD }, + { OV9640_TSLB, OV9640_TSLB_YUYV_UYVY }, + { OV9640_COM16, OV9640_COM16_RB_AVG }, + + /* Gamma curve P */ + { 0x6c, 0x40 }, { 0x6d, 0x30 }, { 0x6e, 0x4b }, { 0x6f, 0x60 }, + { 0x70, 0x70 }, { 0x71, 0x70 }, { 0x72, 0x70 }, { 0x73, 0x70 }, + { 0x74, 0x60 }, { 0x75, 0x60 }, { 0x76, 0x50 }, { 0x77, 0x48 }, + { 0x78, 0x3a }, { 0x79, 0x2e }, { 0x7a, 0x28 }, { 0x7b, 0x22 }, + + /* Gamma curve T */ + { 0x7c, 0x04 }, { 0x7d, 0x07 }, { 0x7e, 0x10 }, { 0x7f, 0x28 }, + { 0x80, 0x36 }, { 0x81, 0x44 }, { 0x82, 0x52 }, { 0x83, 0x60 }, + { 0x84, 0x6c }, { 0x85, 0x78 }, { 0x86, 0x8c }, { 0x87, 0x9e }, + { 0x88, 0xbb }, { 0x89, 0xd2 }, { 0x8a, 0xe6 }, +}; + +/* Configurations + * NOTE: for YUV, alter the following registers: + * COM12 |= OV9640_COM12_YUV_AVG + * + * for RGB, alter the following registers: + * COM7 |= OV9640_COM7_RGB + * COM13 |= OV9640_COM13_RGB_AVG + * COM15 |= proper RGB color encoding mode + */ +static const struct ov9640_reg ov9640_regs_qqcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x0f) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qqvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM1, OV9640_COM1_QQFMT | OV9640_COM1_HREF_2SKIP }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qcif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x07) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QCIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_qvga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM4, OV9640_COM4_QQ_VP | OV9640_COM4_RSVD }, + { OV9640_COM7, OV9640_COM7_QVGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_cif[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x03) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_CIF }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_vga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, OV9640_COM7_VGA }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_sxga[] = { + { OV9640_CLKRC, OV9640_CLKRC_DPLL_EN | OV9640_CLKRC_DIV(0x01) }, + { OV9640_COM3, OV9640_COM3_VP }, + { OV9640_COM7, 0 }, + { OV9640_COM12, OV9640_COM12_RSVD }, + { OV9640_COM13, OV9640_COM13_GAMMA_RAW | OV9640_COM13_MATRIX_EN }, + { OV9640_COM15, OV9640_COM15_OR_10F0 }, +}; + +static const struct ov9640_reg ov9640_regs_yuv[] = { + { OV9640_MTX1, 0x58 }, + { OV9640_MTX2, 0x48 }, + { OV9640_MTX3, 0x10 }, + { OV9640_MTX4, 0x28 }, + { OV9640_MTX5, 0x48 }, + { OV9640_MTX6, 0x70 }, + { OV9640_MTX7, 0x40 }, + { OV9640_MTX8, 0x40 }, + { OV9640_MTX9, 0x40 }, + { OV9640_MTXS, 0x0f }, +}; + +static const struct ov9640_reg ov9640_regs_rgb[] = { + { OV9640_MTX1, 0x71 }, + { OV9640_MTX2, 0x3e }, + { OV9640_MTX3, 0x0c }, + { OV9640_MTX4, 0x33 }, + { OV9640_MTX5, 0x72 }, + { OV9640_MTX6, 0x00 }, + { OV9640_MTX7, 0x2b }, + { OV9640_MTX8, 0x66 }, + { OV9640_MTX9, 0xd2 }, + { OV9640_MTXS, 0x65 }, +}; + +/* + * TODO: this sensor also supports RGB555 and RGB565 formats, but support for + * them has not yet been sufficiently tested and so it is not included with + * this version of the driver. To test and debug these formats add two entries + * to the below array, see ov722x.c for an example. + */ +static const struct soc_camera_data_format ov9640_fmt_lists[] = { + { + .name = "UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .colorspace = V4L2_COLORSPACE_JPEG, + }, +}; + +static const struct v4l2_queryctrl ov9640_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + +/* read a register */ +static int ov9640_reg_read(struct i2c_client *client, u8 reg, u8 *val) +{ + int ret; + u8 data = reg; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = &data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + msg.flags = I2C_M_RD; + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) + goto err; + + *val = data; + return 0; + +err: + dev_err(&client->dev, "Failed reading register 0x%02x!\n", reg); + return ret; +} + +/* write a register */ +static int ov9640_reg_write(struct i2c_client *client, u8 reg, u8 val) +{ + int ret; + u8 _val; + unsigned char data[2] = { reg, val }; + struct i2c_msg msg = { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = data, + }; + + ret = i2c_transfer(client->adapter, &msg, 1); + if (ret < 0) { + dev_err(&client->dev, "Failed writing register 0x%02x!\n", reg); + return ret; + } + + /* we have to read the register back ... no idea why, maybe HW bug */ + ret = ov9640_reg_read(client, reg, &_val); + if (ret) + dev_err(&client->dev, + "Failed reading back register 0x%02x!\n", reg); + + return 0; +} + + +/* Read a register, alter its bits, write it back */ +static int ov9640_reg_rmw(struct i2c_client *client, u8 reg, u8 set, u8 unset) +{ + u8 val; + int ret; + + ret = ov9640_reg_read(client, reg, &val); + if (ret) { + dev_err(&client->dev, + "[Read]-Modify-Write of register %02x failed!\n", reg); + return val; + } + + val |= set; + val &= ~unset; + + ret = ov9640_reg_write(client, reg, val); + if (ret) + dev_err(&client->dev, + "Read-Modify-[Write] of register %02x failed!\n", reg); + + return ret; +} + +/* Soft reset the camera. This has nothing to do with the RESET pin! */ +static int ov9640_reset(struct i2c_client *client) +{ + int ret; + + ret = ov9640_reg_write(client, OV9640_COM7, OV9640_COM7_SCCB_RESET); + if (ret) + dev_err(&client->dev, + "An error occured while entering soft reset!\n"); + + return ret; +} + +/* Start/Stop streaming from the device */ +static int ov9640_s_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + +/* Alter bus settings on camera side */ +static int ov9640_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + return 0; +} + +/* Request bus settings on camera side */ +static unsigned long ov9640_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + + /* + * REVISIT: the camera probably can do 10 bit transfers, but I don't + * have those pins connected on my hardware. + */ + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +/* Get status of additional camera capabilities */ +static int ov9640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + ctrl->value = priv->flag_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = priv->flag_hflip; + break; + } + return 0; +} + +/* Set status of additional camera capabilities */ +static int ov9640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + priv->flag_vflip = ctrl->value; + if (ctrl->value) + ret = ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_V, 0); + else + ret = ov9640_reg_rmw(client, OV9640_MVFP, + 0, OV9640_MVFP_V); + break; + case V4L2_CID_HFLIP: + priv->flag_hflip = ctrl->value; + if (ctrl->value) + ret = ov9640_reg_rmw(client, OV9640_MVFP, + OV9640_MVFP_H, 0); + else + ret = ov9640_reg_rmw(client, OV9640_MVFP, + 0, OV9640_MVFP_H); + break; + } + + return ret; +} + +/* Get chip identification */ +static int ov9640_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = sd->priv; + struct ov9640_priv *priv = container_of(i2c_get_clientdata(client), + struct ov9640_priv, subdev); + + id->ident = priv->model; + id->revision = priv->revision; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov9640_get_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + int ret; + u8 val; + + if (reg->reg & ~0xff) + return -EINVAL; + + reg->size = 1; + + ret = ov9640_reg_read(client, reg->reg, &val); + if (ret) + return ret; + + reg->val = (__u64)val; + + return 0; +} + +static int ov9640_set_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->reg & ~0xff || reg->val & ~0xff) + return -EINVAL; + + return ov9640_reg_write(client, reg->reg, reg->val); +} +#endif + +/* select nearest higher resolution for capture */ +static void ov9640_res_roundup(u32 *width, u32 *height) +{ + int i; + enum { QQCIF, QQVGA, QCIF, QVGA, CIF, VGA, SXGA }; + int res_x[] = { 88, 160, 176, 320, 352, 640, 1280 }; + int res_y[] = { 72, 120, 144, 240, 288, 480, 960 }; + + for (i = 0; i < ARRAY_SIZE(res_x); i++) { + if (res_x[i] >= *width && res_y[i] >= *height) { + *width = res_x[i]; + *height = res_y[i]; + return; + } + } + + *width = res_x[SXGA]; + *height = res_y[SXGA]; +} + +/* Prepare necessary register changes depending on color encoding */ +static void ov9640_alter_regs(u32 pixfmt, struct ov9640_reg_alt *alt) +{ + switch (pixfmt) { + case V4L2_PIX_FMT_UYVY: + alt->com12 = OV9640_COM12_YUV_AVG; + alt->com13 = OV9640_COM13_Y_DELAY_EN | + OV9640_COM13_YUV_DLY(0x01); + break; + case V4L2_PIX_FMT_RGB555: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_555; + break; + case V4L2_PIX_FMT_RGB565: + alt->com7 = OV9640_COM7_RGB; + alt->com13 = OV9640_COM13_RGB_AVG; + alt->com15 = OV9640_COM15_RGB_565; + break; + }; +} + +/* Setup registers according to resolution and color encoding */ +static int ov9640_write_regs(struct i2c_client *client, + u32 width, u32 pixfmt, struct ov9640_reg_alt *alts) +{ + const struct ov9640_reg *ov9640_regs, *matrix_regs; + int ov9640_regs_len, matrix_regs_len; + int i, ret; + u8 val; + + /* select register configuration for given resolution */ + switch (width) { + case W_QQCIF: + ov9640_regs = ov9640_regs_qqcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqcif); + break; + case W_QQVGA: + ov9640_regs = ov9640_regs_qqvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qqvga); + break; + case W_QCIF: + ov9640_regs = ov9640_regs_qcif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qcif); + break; + case W_QVGA: + ov9640_regs = ov9640_regs_qvga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_qvga); + break; + case W_CIF: + ov9640_regs = ov9640_regs_cif; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_cif); + break; + case W_VGA: + ov9640_regs = ov9640_regs_vga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_vga); + break; + case W_SXGA: + ov9640_regs = ov9640_regs_sxga; + ov9640_regs_len = ARRAY_SIZE(ov9640_regs_sxga); + break; + default: + dev_err(&client->dev, "Failed to select resolution!\n"); + return -EINVAL; + } + + /* select color matrix configuration for given color encoding */ + if (pixfmt == V4L2_PIX_FMT_UYVY) { + matrix_regs = ov9640_regs_yuv; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_yuv); + } else { + matrix_regs = ov9640_regs_rgb; + matrix_regs_len = ARRAY_SIZE(ov9640_regs_rgb); + } + + /* write register settings into the module */ + for (i = 0; i < ov9640_regs_len; i++) { + val = ov9640_regs[i].val; + + switch (ov9640_regs[i].reg) { + case OV9640_COM7: + val |= alts->com7; + break; + case OV9640_COM12: + val |= alts->com12; + break; + case OV9640_COM13: + val |= alts->com13; + break; + case OV9640_COM15: + val |= alts->com15; + break; + } + + ret = ov9640_reg_write(client, ov9640_regs[i].reg, val); + if (ret) + return ret; + } + + /* write color matrix configuration into the module */ + for (i = 0; i < matrix_regs_len; i++) { + ret = ov9640_reg_write(client, matrix_regs[i].reg, + matrix_regs[i].val); + if (ret) + return ret; + } + + return 0; +} + +/* program default register values */ +static int ov9640_prog_dflt(struct i2c_client *client) +{ + int i, ret; + + for (i = 0; i < ARRAY_SIZE(ov9640_regs_dflt); i++) { + ret = ov9640_reg_write(client, ov9640_regs_dflt[i].reg, + ov9640_regs_dflt[i].val); + if (ret) + return ret; + } + + /* wait for the changes to actually happen, 140ms are not enough yet */ + mdelay(150); + + return 0; +} + +/* set the format we will capture in */ +static int ov9640_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct v4l2_pix_format *pix = &f->fmt.pix; + struct ov9640_reg_alt alts = {0}; + int ret; + + ov9640_res_roundup(&pix->width, &pix->height); + ov9640_alter_regs(pix->pixelformat, &alts); + + ov9640_reset(client); + + ret = ov9640_prog_dflt(client); + if (ret) + return ret; + + return ov9640_write_regs(client, pix->width, pix->pixelformat, &alts); +} + +static int ov9640_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + ov9640_res_roundup(&pix->width, &pix->height); + pix->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov9640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + a->c.left = 0; + a->c.top = 0; + a->c.width = W_SXGA; + a->c.height = H_SXGA; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int ov9640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = W_SXGA; + a->bounds.height = H_SXGA; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + + + +static int ov9640_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + struct ov9640_priv *priv = i2c_get_clientdata(client); + u8 pid, ver, midh, midl; + const char *devname; + int ret = 0; + + /* + * We must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { + dev_err(&client->dev, "Parent missing or invalid!\n"); + ret = -ENODEV; + goto err; + } + + icd->formats = ov9640_fmt_lists; + icd->num_formats = ARRAY_SIZE(ov9640_fmt_lists); + + /* + * check and show product ID and manufacturer ID + */ + + ret = ov9640_reg_read(client, OV9640_PID, &pid); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_VER, &ver); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_MIDH, &midh); + if (ret) + goto err; + + ret = ov9640_reg_read(client, OV9640_MIDL, &midl); + if (ret) + goto err; + + switch (VERSION(pid, ver)) { + case OV9640_V2: + devname = "ov9640"; + priv->model = V4L2_IDENT_OV9640; + priv->revision = 2; + case OV9640_V3: + devname = "ov9640"; + priv->model = V4L2_IDENT_OV9640; + priv->revision = 3; + break; + default: + dev_err(&client->dev, "Product ID error %x:%x\n", pid, ver); + ret = -ENODEV; + goto err; + } + + dev_info(&client->dev, "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, pid, ver, midh, midl); + +err: + return ret; +} + +static struct soc_camera_ops ov9640_ops = { + .set_bus_param = ov9640_set_bus_param, + .query_bus_param = ov9640_query_bus_param, + .controls = ov9640_controls, + .num_controls = ARRAY_SIZE(ov9640_controls), +}; + +static struct v4l2_subdev_core_ops ov9640_core_ops = { + .g_ctrl = ov9640_g_ctrl, + .s_ctrl = ov9640_s_ctrl, + .g_chip_ident = ov9640_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov9640_get_register, + .s_register = ov9640_set_register, +#endif + +}; + +static struct v4l2_subdev_video_ops ov9640_video_ops = { + .s_stream = ov9640_s_stream, + .s_fmt = ov9640_s_fmt, + .try_fmt = ov9640_try_fmt, + .cropcap = ov9640_cropcap, + .g_crop = ov9640_g_crop, + +}; + +static struct v4l2_subdev_ops ov9640_subdev_ops = { + .core = &ov9640_core_ops, + .video = &ov9640_video_ops, +}; + +/* + * i2c_driver function + */ +static int ov9640_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov9640_priv *priv; + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "Missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "Missing platform_data for driver\n"); + return -EINVAL; + } + + priv = kzalloc(sizeof(struct ov9640_priv), GFP_KERNEL); + if (!priv) { + dev_err(&client->dev, + "Failed to allocate memory for private data!\n"); + return -ENOMEM; + } + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov9640_subdev_ops); + + icd->ops = &ov9640_ops; + + ret = ov9640_video_probe(icd, client); + + if (ret) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(priv); + } + + return ret; +} + +static int ov9640_remove(struct i2c_client *client) +{ + struct ov9640_priv *priv = i2c_get_clientdata(client); + + i2c_set_clientdata(client, NULL); + kfree(priv); + return 0; +} + +static const struct i2c_device_id ov9640_id[] = { + { "ov9640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov9640_id); + +static struct i2c_driver ov9640_i2c_driver = { + .driver = { + .name = "ov9640", + }, + .probe = ov9640_probe, + .remove = ov9640_remove, + .id_table = ov9640_id, +}; + +static int __init ov9640_module_init(void) +{ + return i2c_add_driver(&ov9640_i2c_driver); +} + +static void __exit ov9640_module_exit(void) +{ + i2c_del_driver(&ov9640_i2c_driver); +} + +module_init(ov9640_module_init); +module_exit(ov9640_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for OmniVision OV96xx"); +MODULE_AUTHOR("Marek Vasut "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/ov9640.h b/drivers/media/video/ov9640.h new file mode 100644 index 0000000000000..f8a51b70792e1 --- /dev/null +++ b/drivers/media/video/ov9640.h @@ -0,0 +1,209 @@ +/* + * OmniVision OV96xx Camera Header File + * + * Copyright (C) 2009 Marek Vasut + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __DRIVERS_MEDIA_VIDEO_OV9640_H__ +#define __DRIVERS_MEDIA_VIDEO_OV9640_H__ + +/* Register definitions */ +#define OV9640_GAIN 0x00 +#define OV9640_BLUE 0x01 +#define OV9640_RED 0x02 +#define OV9640_VFER 0x03 +#define OV9640_COM1 0x04 +#define OV9640_BAVE 0x05 +#define OV9640_GEAVE 0x06 +#define OV9640_RSID 0x07 +#define OV9640_RAVE 0x08 +#define OV9640_COM2 0x09 +#define OV9640_PID 0x0a +#define OV9640_VER 0x0b +#define OV9640_COM3 0x0c +#define OV9640_COM4 0x0d +#define OV9640_COM5 0x0e +#define OV9640_COM6 0x0f +#define OV9640_AECH 0x10 +#define OV9640_CLKRC 0x11 +#define OV9640_COM7 0x12 +#define OV9640_COM8 0x13 +#define OV9640_COM9 0x14 +#define OV9640_COM10 0x15 +/* 0x16 - RESERVED */ +#define OV9640_HSTART 0x17 +#define OV9640_HSTOP 0x18 +#define OV9640_VSTART 0x19 +#define OV9640_VSTOP 0x1a +#define OV9640_PSHFT 0x1b +#define OV9640_MIDH 0x1c +#define OV9640_MIDL 0x1d +#define OV9640_MVFP 0x1e +#define OV9640_LAEC 0x1f +#define OV9640_BOS 0x20 +#define OV9640_GBOS 0x21 +#define OV9640_GROS 0x22 +#define OV9640_ROS 0x23 +#define OV9640_AEW 0x24 +#define OV9640_AEB 0x25 +#define OV9640_VPT 0x26 +#define OV9640_BBIAS 0x27 +#define OV9640_GBBIAS 0x28 +/* 0x29 - RESERVED */ +#define OV9640_EXHCH 0x2a +#define OV9640_EXHCL 0x2b +#define OV9640_RBIAS 0x2c +#define OV9640_ADVFL 0x2d +#define OV9640_ADVFH 0x2e +#define OV9640_YAVE 0x2f +#define OV9640_HSYST 0x30 +#define OV9640_HSYEN 0x31 +#define OV9640_HREF 0x32 +#define OV9640_CHLF 0x33 +#define OV9640_ARBLM 0x34 +/* 0x35..0x36 - RESERVED */ +#define OV9640_ADC 0x37 +#define OV9640_ACOM 0x38 +#define OV9640_OFON 0x39 +#define OV9640_TSLB 0x3a +#define OV9640_COM11 0x3b +#define OV9640_COM12 0x3c +#define OV9640_COM13 0x3d +#define OV9640_COM14 0x3e +#define OV9640_EDGE 0x3f +#define OV9640_COM15 0x40 +#define OV9640_COM16 0x41 +#define OV9640_COM17 0x42 +/* 0x43..0x4e - RESERVED */ +#define OV9640_MTX1 0x4f +#define OV9640_MTX2 0x50 +#define OV9640_MTX3 0x51 +#define OV9640_MTX4 0x52 +#define OV9640_MTX5 0x53 +#define OV9640_MTX6 0x54 +#define OV9640_MTX7 0x55 +#define OV9640_MTX8 0x56 +#define OV9640_MTX9 0x57 +#define OV9640_MTXS 0x58 +/* 0x59..0x61 - RESERVED */ +#define OV9640_LCC1 0x62 +#define OV9640_LCC2 0x63 +#define OV9640_LCC3 0x64 +#define OV9640_LCC4 0x65 +#define OV9640_LCC5 0x66 +#define OV9640_MANU 0x67 +#define OV9640_MANV 0x68 +#define OV9640_HV 0x69 +#define OV9640_MBD 0x6a +#define OV9640_DBLV 0x6b +#define OV9640_GSP 0x6c /* ... till 0x7b */ +#define OV9640_GST 0x7c /* ... till 0x8a */ + +#define OV9640_CLKRC_DPLL_EN 0x80 +#define OV9640_CLKRC_DIRECT 0x40 +#define OV9640_CLKRC_DIV(x) ((x) & 0x3f) + +#define OV9640_PSHFT_VAL(x) ((x) & 0xff) + +#define OV9640_ACOM_2X_ANALOG 0x80 +#define OV9640_ACOM_RSVD 0x12 + +#define OV9640_MVFP_V 0x10 +#define OV9640_MVFP_H 0x20 + +#define OV9640_COM1_HREF_NOSKIP 0x00 +#define OV9640_COM1_HREF_2SKIP 0x04 +#define OV9640_COM1_HREF_3SKIP 0x08 +#define OV9640_COM1_QQFMT 0x20 + +#define OV9640_COM2_SSM 0x10 + +#define OV9640_COM3_VP 0x04 + +#define OV9640_COM4_QQ_VP 0x80 +#define OV9640_COM4_RSVD 0x40 + +#define OV9640_COM5_SYSCLK 0x80 +#define OV9640_COM5_LONGEXP 0x01 + +#define OV9640_COM6_OPT_BLC 0x40 +#define OV9640_COM6_ADBLC_BIAS 0x08 +#define OV9640_COM6_FMT_RST 0x82 +#define OV9640_COM6_ADBLC_OPTEN 0x01 + +#define OV9640_COM7_RAW_RGB 0x01 +#define OV9640_COM7_RGB 0x04 +#define OV9640_COM7_QCIF 0x08 +#define OV9640_COM7_QVGA 0x10 +#define OV9640_COM7_CIF 0x20 +#define OV9640_COM7_VGA 0x40 +#define OV9640_COM7_SCCB_RESET 0x80 + +#define OV9640_TSLB_YVYU_YUYV 0x04 +#define OV9640_TSLB_YUYV_UYVY 0x08 + +#define OV9640_COM12_YUV_AVG 0x04 +#define OV9640_COM12_RSVD 0x40 + +#define OV9640_COM13_GAMMA_NONE 0x00 +#define OV9640_COM13_GAMMA_Y 0x40 +#define OV9640_COM13_GAMMA_RAW 0x80 +#define OV9640_COM13_RGB_AVG 0x20 +#define OV9640_COM13_MATRIX_EN 0x10 +#define OV9640_COM13_Y_DELAY_EN 0x08 +#define OV9640_COM13_YUV_DLY(x) ((x) & 0x07) + +#define OV9640_COM15_OR_00FF 0x00 +#define OV9640_COM15_OR_01FE 0x40 +#define OV9640_COM15_OR_10F0 0xc0 +#define OV9640_COM15_RGB_NORM 0x00 +#define OV9640_COM15_RGB_565 0x10 +#define OV9640_COM15_RGB_555 0x30 + +#define OV9640_COM16_RB_AVG 0x01 + +/* IDs */ +#define OV9640_V2 0x9648 +#define OV9640_V3 0x9649 +#define VERSION(pid, ver) (((pid) << 8) | ((ver) & 0xFF)) + +/* supported resolutions */ +enum { + W_QQCIF = 88, + W_QQVGA = 160, + W_QCIF = 176, + W_QVGA = 320, + W_CIF = 352, + W_VGA = 640, + W_SXGA = 1280 +}; +#define H_SXGA 960 + +/* Misc. structures */ +struct ov9640_reg_alt { + u8 com7; + u8 com12; + u8 com13; + u8 com15; +}; + +struct ov9640_reg { + u8 reg; + u8 val; +}; + +struct ov9640_priv { + struct v4l2_subdev subdev; + + int model; + int revision; + + bool flag_vflip; + bool flag_hflip; +}; + +#endif /* __DRIVERS_MEDIA_VIDEO_OV9640_H__ */ diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index a1ad38fc49c18..73ec970ca5ca0 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -14,8 +14,10 @@ * unless the userspace driver also doesn't work for you... * * Changes: - * 08/07/2003 Daniele Bellucci - * - pms_capture: report back -EFAULT + * 25-11-2009 Hans Verkuil + * - converted to version 2 of the V4L API. + * 08/07/2003 Daniele Bellucci + * - pms_capture: report back -EFAULT */ #include @@ -27,175 +29,183 @@ #include #include #include +#include +#include +#include #include -#include + +#include #include #include -#include +#include -#include +MODULE_LICENSE("GPL"); #define MOTOROLA 1 -#define PHILIPS2 2 +#define PHILIPS2 2 /* SAA7191 */ #define PHILIPS1 3 #define MVVMEMORYWIDTH 0x40 /* 512 bytes */ -struct pms_device -{ - struct video_device v; - struct video_picture picture; - int height; - int width; - unsigned long in_use; - struct mutex lock; -}; - -struct i2c_info -{ +struct i2c_info { u8 slave; u8 sub; u8 data; u8 hits; }; -static int i2c_count; -static struct i2c_info i2cinfo[64]; +struct pms { + struct v4l2_device v4l2_dev; + struct video_device vdev; + int height; + int width; + int depth; + int input; + s32 brightness, saturation, hue, contrast; + unsigned long in_use; + struct mutex lock; + int i2c_count; + struct i2c_info i2cinfo[64]; + + int decoder; + int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ + v4l2_std_id std; + int io; + int data; + void __iomem *mem; +}; -static int decoder = PHILIPS2; -static int standard; /* 0 - auto 1 - ntsc 2 - pal 3 - secam */ +static struct pms pms_card; /* * I/O ports and Shared Memory */ -static int io_port = 0x250; -static int data_port = 0x251; -static int mem_base = 0xC8000; -static void __iomem *mem; -static int video_nr = -1; +static int io_port = 0x250; +module_param(io_port, int, 0); +static int mem_base = 0xc8000; +module_param(mem_base, int, 0); +static int video_nr = -1; +module_param(video_nr, int, 0); -static inline void mvv_write(u8 index, u8 value) + +static inline void mvv_write(struct pms *dev, u8 index, u8 value) { - outw(index|(value<<8), io_port); + outw(index | (value << 8), dev->io); } -static inline u8 mvv_read(u8 index) +static inline u8 mvv_read(struct pms *dev, u8 index) { - outb(index, io_port); - return inb(data_port); + outb(index, dev->io); + return inb(dev->data); } -static int pms_i2c_stat(u8 slave) +static int pms_i2c_stat(struct pms *dev, u8 slave) { - int counter; + int counter = 0; int i; - outb(0x28, io_port); + outb(0x28, dev->io); - counter=0; - while((inb(data_port)&0x01)==0) - if(counter++==256) + while ((inb(dev->data) & 0x01) == 0) + if (counter++ == 256) break; - while((inb(data_port)&0x01)!=0) - if(counter++==256) + while ((inb(dev->data) & 0x01) != 0) + if (counter++ == 256) break; - outb(slave, io_port); + outb(slave, dev->io); - counter=0; - while((inb(data_port)&0x01)==0) - if(counter++==256) + counter = 0; + while ((inb(dev->data) & 0x01) == 0) + if (counter++ == 256) break; - while((inb(data_port)&0x01)!=0) - if(counter++==256) + while ((inb(dev->data) & 0x01) != 0) + if (counter++ == 256) break; - for(i=0;i<12;i++) - { - char st=inb(data_port); - if((st&2)!=0) + for (i = 0; i < 12; i++) { + char st = inb(dev->data); + + if ((st & 2) != 0) return -1; - if((st&1)==0) + if ((st & 1) == 0) break; } - outb(0x29, io_port); - return inb(data_port); + outb(0x29, dev->io); + return inb(dev->data); } -static int pms_i2c_write(u16 slave, u16 sub, u16 data) +static int pms_i2c_write(struct pms *dev, u16 slave, u16 sub, u16 data) { - int skip=0; + int skip = 0; int count; int i; - for(i=0;ii2c_count; i++) { + if ((dev->i2cinfo[i].slave == slave) && + (dev->i2cinfo[i].sub == sub)) { + if (dev->i2cinfo[i].data == data) + skip = 1; + dev->i2cinfo[i].data = data; + i = dev->i2c_count + 1; } } - if(i==i2c_count && i2c_count<64) - { - i2cinfo[i2c_count].slave=slave; - i2cinfo[i2c_count].sub=sub; - i2cinfo[i2c_count].data=data; - i2c_count++; + if (i == dev->i2c_count && dev->i2c_count < 64) { + dev->i2cinfo[dev->i2c_count].slave = slave; + dev->i2cinfo[dev->i2c_count].sub = sub; + dev->i2cinfo[dev->i2c_count].data = data; + dev->i2c_count++; } - if(skip) + if (skip) return 0; - mvv_write(0x29, sub); - mvv_write(0x2A, data); - mvv_write(0x28, slave); + mvv_write(dev, 0x29, sub); + mvv_write(dev, 0x2A, data); + mvv_write(dev, 0x28, slave); - outb(0x28, io_port); + outb(0x28, dev->io); - count=0; - while((inb(data_port)&1)==0) - if(count>255) + count = 0; + while ((inb(dev->data) & 1) == 0) + if (count > 255) break; - while((inb(data_port)&1)!=0) - if(count>255) + while ((inb(dev->data) & 1) != 0) + if (count > 255) break; - count=inb(data_port); + count = inb(dev->data); - if(count&2) + if (count & 2) return -1; return count; } -static int pms_i2c_read(int slave, int sub) +static int pms_i2c_read(struct pms *dev, int slave, int sub) { - int i=0; - for(i=0;ii2c_count; i++) { + if (dev->i2cinfo[i].slave == slave && dev->i2cinfo[i].sub == sub) + return dev->i2cinfo[i].data; } return 0; } -static void pms_i2c_andor(int slave, int sub, int and, int or) +static void pms_i2c_andor(struct pms *dev, int slave, int sub, int and, int or) { u8 tmp; - tmp=pms_i2c_read(slave, sub); - tmp = (tmp&and)|or; - pms_i2c_write(slave, sub, tmp); + tmp = pms_i2c_read(dev, slave, sub); + tmp = (tmp & and) | or; + pms_i2c_write(dev, slave, sub, tmp); } /* @@ -203,100 +213,108 @@ static void pms_i2c_andor(int slave, int sub, int and, int or) */ -static void pms_videosource(short source) +static void pms_videosource(struct pms *dev, short source) { - mvv_write(0x2E, source?0x31:0x30); + switch (dev->decoder) { + case MOTOROLA: + break; + case PHILIPS2: + pms_i2c_andor(dev, 0x8a, 0x06, 0x7f, source ? 0x80 : 0); + break; + case PHILIPS1: + break; + } + mvv_write(dev, 0x2E, 0x31); + /* Was: mvv_write(dev, 0x2E, source ? 0x31 : 0x30); + But could not make this work correctly. Only Composite input + worked for me. */ } -static void pms_hue(short hue) +static void pms_hue(struct pms *dev, short hue) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, hue); - break; - case PHILIPS2: - pms_i2c_write(0x8A, 0x07, hue); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x07, hue); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, hue); + break; + case PHILIPS2: + pms_i2c_write(dev, 0x8a, 0x07, hue); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x07, hue); + break; } } -static void pms_colour(short colour) +static void pms_saturation(struct pms *dev, short sat) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, colour); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x12, colour); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, sat); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x12, sat); + break; } } -static void pms_contrast(short contrast) +static void pms_contrast(struct pms *dev, short contrast) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, contrast); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x13, contrast); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, contrast); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x13, contrast); + break; } } -static void pms_brightness(short brightness) +static void pms_brightness(struct pms *dev, short brightness) { - switch(decoder) - { - case MOTOROLA: - pms_i2c_write(0x8A, 0x00, brightness); - pms_i2c_write(0x8A, 0x00, brightness); - pms_i2c_write(0x8A, 0x00, brightness); - break; - case PHILIPS1: - pms_i2c_write(0x42, 0x19, brightness); - break; + switch (dev->decoder) { + case MOTOROLA: + pms_i2c_write(dev, 0x8a, 0x00, brightness); + pms_i2c_write(dev, 0x8a, 0x00, brightness); + pms_i2c_write(dev, 0x8a, 0x00, brightness); + break; + case PHILIPS1: + pms_i2c_write(dev, 0x42, 0x19, brightness); + break; } } -static void pms_format(short format) +static void pms_format(struct pms *dev, short format) { int target; - standard = format; - if(decoder==PHILIPS1) - target=0x42; - else if(decoder==PHILIPS2) - target=0x8A; + dev->standard = format; + + if (dev->decoder == PHILIPS1) + target = 0x42; + else if (dev->decoder == PHILIPS2) + target = 0x8a; else return; - switch(format) - { - case 0: /* Auto */ - pms_i2c_andor(target, 0x0D, 0xFE,0x00); - pms_i2c_andor(target, 0x0F, 0x3F,0x80); - break; - case 1: /* NTSC */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x00); - pms_i2c_andor(target, 0x0F, 0x3F, 0x40); - break; - case 2: /* PAL */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x00); - pms_i2c_andor(target, 0x0F, 0x3F, 0x00); - break; - case 3: /* SECAM */ - pms_i2c_andor(target, 0x0D, 0xFE, 0x01); - pms_i2c_andor(target, 0x0F, 0x3F, 0x00); - break; + switch (format) { + case 0: /* Auto */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x80); + break; + case 1: /* NTSC */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x40); + break; + case 2: /* PAL */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x00); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); + break; + case 3: /* SECAM */ + pms_i2c_andor(dev, target, 0x0d, 0xfe, 0x01); + pms_i2c_andor(dev, target, 0x0f, 0x3f, 0x00); + break; } } @@ -308,18 +326,17 @@ static void pms_format(short format) * people need it. We also don't yet use the PMS interrupt. */ -static void pms_hstart(short start) +static void pms_hstart(struct pms *dev, short start) { - switch(decoder) - { - case PHILIPS1: - pms_i2c_write(0x8A, 0x05, start); - pms_i2c_write(0x8A, 0x18, start); - break; - case PHILIPS2: - pms_i2c_write(0x42, 0x05, start); - pms_i2c_write(0x42, 0x18, start); - break; + switch (dev->decoder) { + case PHILIPS1: + pms_i2c_write(dev, 0x8a, 0x05, start); + pms_i2c_write(dev, 0x8a, 0x18, start); + break; + case PHILIPS2: + pms_i2c_write(dev, 0x42, 0x05, start); + pms_i2c_write(dev, 0x42, 0x18, start); + break; } } @@ -327,293 +344,271 @@ static void pms_hstart(short start) * Bandpass filters */ -static void pms_bandpass(short pass) +static void pms_bandpass(struct pms *dev, short pass) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xCF, (pass&0x03)<<4); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xCF, (pass&0x03)<<4); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xcf, (pass & 0x03) << 4); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xcf, (pass & 0x03) << 4); } -static void pms_antisnow(short snow) +static void pms_antisnow(struct pms *dev, short snow) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xF3, (snow&0x03)<<2); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xF3, (snow&0x03)<<2); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xf3, (snow & 0x03) << 2); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xf3, (snow & 0x03) << 2); } -static void pms_sharpness(short sharp) +static void pms_sharpness(struct pms *dev, short sharp) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xFC, sharp&0x03); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xFC, sharp&0x03); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xfc, sharp & 0x03); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xfc, sharp & 0x03); } -static void pms_chromaagc(short agc) +static void pms_chromaagc(struct pms *dev, short agc) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0C, 0x9F, (agc&0x03)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0C, 0x9F, (agc&0x03)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0c, 0x9f, (agc & 0x03) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0c, 0x9f, (agc & 0x03) << 5); } -static void pms_vertnoise(short noise) +static void pms_vertnoise(struct pms *dev, short noise) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x10, 0xFC, noise&3); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x10, 0xFC, noise&3); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x10, 0xfc, noise & 3); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x10, 0xfc, noise & 3); } -static void pms_forcecolour(short colour) +static void pms_forcecolour(struct pms *dev, short colour) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0C, 0x7F, (colour&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0C, 0x7, (colour&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0c, 0x7f, (colour & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0c, 0x7, (colour & 1) << 7); } -static void pms_antigamma(short gamma) +static void pms_antigamma(struct pms *dev, short gamma) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x00, 0x7F, (gamma&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x20, 0x7, (gamma&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x00, 0x7f, (gamma & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x20, 0x7, (gamma & 1) << 7); } -static void pms_prefilter(short filter) +static void pms_prefilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x06, 0xBF, (filter&1)<<6); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x06, 0xBF, (filter&1)<<6); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x06, 0xbf, (filter & 1) << 6); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x06, 0xbf, (filter & 1) << 6); } -static void pms_hfilter(short filter) +static void pms_hfilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x04, 0x1F, (filter&7)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x24, 0x1F, (filter&7)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x04, 0x1f, (filter & 7) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x24, 0x1f, (filter & 7) << 5); } -static void pms_vfilter(short filter) +static void pms_vfilter(struct pms *dev, short filter) { - if(decoder==PHILIPS2) - pms_i2c_andor(0xB8, 0x08, 0x9F, (filter&3)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x28, 0x9F, (filter&3)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0xb8, 0x08, 0x9f, (filter & 3) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x28, 0x9f, (filter & 3) << 5); } -static void pms_killcolour(short colour) +static void pms_killcolour(struct pms *dev, short colour) { - if(decoder==PHILIPS2) - { - pms_i2c_andor(0x8A, 0x08, 0x07, (colour&0x1F)<<3); - pms_i2c_andor(0x8A, 0x09, 0x07, (colour&0x1F)<<3); - } - else if(decoder==PHILIPS1) - { - pms_i2c_andor(0x42, 0x08, 0x07, (colour&0x1F)<<3); - pms_i2c_andor(0x42, 0x09, 0x07, (colour&0x1F)<<3); + if (dev->decoder == PHILIPS2) { + pms_i2c_andor(dev, 0x8a, 0x08, 0x07, (colour & 0x1f) << 3); + pms_i2c_andor(dev, 0x8a, 0x09, 0x07, (colour & 0x1f) << 3); + } else if (dev->decoder == PHILIPS1) { + pms_i2c_andor(dev, 0x42, 0x08, 0x07, (colour & 0x1f) << 3); + pms_i2c_andor(dev, 0x42, 0x09, 0x07, (colour & 0x1f) << 3); } } -static void pms_chromagain(short chroma) +static void pms_chromagain(struct pms *dev, short chroma) { - if(decoder==PHILIPS2) - { - pms_i2c_write(0x8A, 0x11, chroma); - } - else if(decoder==PHILIPS1) - { - pms_i2c_write(0x42, 0x11, chroma); - } + if (dev->decoder == PHILIPS2) + pms_i2c_write(dev, 0x8a, 0x11, chroma); + else if (dev->decoder == PHILIPS1) + pms_i2c_write(dev, 0x42, 0x11, chroma); } -static void pms_spacialcompl(short data) +static void pms_spacialcompl(struct pms *dev, short data) { - mvv_write(0x3B, data); + mvv_write(dev, 0x3b, data); } -static void pms_spacialcomph(short data) +static void pms_spacialcomph(struct pms *dev, short data) { - mvv_write(0x3A, data); + mvv_write(dev, 0x3a, data); } -static void pms_vstart(short start) +static void pms_vstart(struct pms *dev, short start) { - mvv_write(0x16, start); - mvv_write(0x17, (start>>8)&0x01); + mvv_write(dev, 0x16, start); + mvv_write(dev, 0x17, (start >> 8) & 0x01); } #endif -static void pms_secamcross(short cross) +static void pms_secamcross(struct pms *dev, short cross) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A, 0x0F, 0xDF, (cross&1)<<5); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42, 0x0F, 0xDF, (cross&1)<<5); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0f, 0xdf, (cross & 1) << 5); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0f, 0xdf, (cross & 1) << 5); } -static void pms_swsense(short sense) +static void pms_swsense(struct pms *dev, short sense) { - if(decoder==PHILIPS2) - { - pms_i2c_write(0x8A, 0x0A, sense); - pms_i2c_write(0x8A, 0x0B, sense); - } - else if(decoder==PHILIPS1) - { - pms_i2c_write(0x42, 0x0A, sense); - pms_i2c_write(0x42, 0x0B, sense); + if (dev->decoder == PHILIPS2) { + pms_i2c_write(dev, 0x8a, 0x0a, sense); + pms_i2c_write(dev, 0x8a, 0x0b, sense); + } else if (dev->decoder == PHILIPS1) { + pms_i2c_write(dev, 0x42, 0x0a, sense); + pms_i2c_write(dev, 0x42, 0x0b, sense); } } -static void pms_framerate(short frr) +static void pms_framerate(struct pms *dev, short frr) { - int fps=(standard==1)?30:25; - if(frr==0) + int fps = (dev->std & V4L2_STD_525_60) ? 30 : 25; + + if (frr == 0) return; - fps=fps/frr; - mvv_write(0x14,0x80|fps); - mvv_write(0x15,1); + fps = fps/frr; + mvv_write(dev, 0x14, 0x80 | fps); + mvv_write(dev, 0x15, 1); } -static void pms_vert(u8 deciden, u8 decinum) +static void pms_vert(struct pms *dev, u8 deciden, u8 decinum) { - mvv_write(0x1C, deciden); /* Denominator */ - mvv_write(0x1D, decinum); /* Numerator */ + mvv_write(dev, 0x1c, deciden); /* Denominator */ + mvv_write(dev, 0x1d, decinum); /* Numerator */ } /* * Turn 16bit ratios into best small ratio the chipset can grok */ -static void pms_vertdeci(unsigned short decinum, unsigned short deciden) +static void pms_vertdeci(struct pms *dev, unsigned short decinum, unsigned short deciden) { - /* Knock it down by /5 once */ - if(decinum%5==0) - { - deciden/=5; - decinum/=5; + /* Knock it down by / 5 once */ + if (decinum % 5 == 0) { + deciden /= 5; + decinum /= 5; } /* * 3's */ - while(decinum%3==0 && deciden%3==0) - { - deciden/=3; - decinum/=3; + while (decinum % 3 == 0 && deciden % 3 == 0) { + deciden /= 3; + decinum /= 3; } /* * 2's */ - while(decinum%2==0 && deciden%2==0) - { - decinum/=2; - deciden/=2; + while (decinum % 2 == 0 && deciden % 2 == 0) { + decinum /= 2; + deciden /= 2; } /* * Fudgyify */ - while(deciden>32) - { - deciden/=2; - decinum=(decinum+1)/2; + while (deciden > 32) { + deciden /= 2; + decinum = (decinum + 1) / 2; } - if(deciden==32) + if (deciden == 32) deciden--; - pms_vert(deciden,decinum); + pms_vert(dev, deciden, decinum); } -static void pms_horzdeci(short decinum, short deciden) +static void pms_horzdeci(struct pms *dev, short decinum, short deciden) { - if(decinum<=512) - { - if(decinum%5==0) - { - decinum/=5; - deciden/=5; + if (decinum <= 512) { + if (decinum % 5 == 0) { + decinum /= 5; + deciden /= 5; } - } - else - { - decinum=512; - deciden=640; /* 768 would be ideal */ + } else { + decinum = 512; + deciden = 640; /* 768 would be ideal */ } - while(((decinum|deciden)&1)==0) - { - decinum>>=1; - deciden>>=1; + while (((decinum | deciden) & 1) == 0) { + decinum >>= 1; + deciden >>= 1; } - while(deciden>32) - { - deciden>>=1; - decinum=(decinum+1)>>1; + while (deciden > 32) { + deciden >>= 1; + decinum = (decinum + 1) >> 1; } - if(deciden==32) + if (deciden == 32) deciden--; - mvv_write(0x24, 0x80|deciden); - mvv_write(0x25, decinum); + mvv_write(dev, 0x24, 0x80 | deciden); + mvv_write(dev, 0x25, decinum); } -static void pms_resolution(short width, short height) +static void pms_resolution(struct pms *dev, short width, short height) { int fg_height; - fg_height=height; - if(fg_height>280) - fg_height=280; + fg_height = height; + if (fg_height > 280) + fg_height = 280; - mvv_write(0x18, fg_height); - mvv_write(0x19, fg_height>>8); + mvv_write(dev, 0x18, fg_height); + mvv_write(dev, 0x19, fg_height >> 8); - if(standard==1) - { - mvv_write(0x1A, 0xFC); - mvv_write(0x1B, 0x00); - if(height>fg_height) - pms_vertdeci(240,240); + if (dev->std & V4L2_STD_525_60) { + mvv_write(dev, 0x1a, 0xfc); + mvv_write(dev, 0x1b, 0x00); + if (height > fg_height) + pms_vertdeci(dev, 240, 240); else - pms_vertdeci(fg_height,240); - } - else - { - mvv_write(0x1A, 0x1A); - mvv_write(0x1B, 0x01); - if(fg_height>256) - pms_vertdeci(270,270); + pms_vertdeci(dev, fg_height, 240); + } else { + mvv_write(dev, 0x1a, 0x1a); + mvv_write(dev, 0x1b, 0x01); + if (fg_height > 256) + pms_vertdeci(dev, 270, 270); else - pms_vertdeci(fg_height, 270); + pms_vertdeci(dev, fg_height, 270); } - mvv_write(0x12,0); - mvv_write(0x13, MVVMEMORYWIDTH); - mvv_write(0x42, 0x00); - mvv_write(0x43, 0x00); - mvv_write(0x44, MVVMEMORYWIDTH); + mvv_write(dev, 0x12, 0); + mvv_write(dev, 0x13, MVVMEMORYWIDTH); + mvv_write(dev, 0x42, 0x00); + mvv_write(dev, 0x43, 0x00); + mvv_write(dev, 0x44, MVVMEMORYWIDTH); - mvv_write(0x22, width+8); - mvv_write(0x23, (width+8)>> 8); + mvv_write(dev, 0x22, width + 8); + mvv_write(dev, 0x23, (width + 8) >> 8); - if(standard==1) - pms_horzdeci(width,640); + if (dev->std & V4L2_STD_525_60) + pms_horzdeci(dev, width, 640); else - pms_horzdeci(width+8, 768); + pms_horzdeci(dev, width + 8, 768); - mvv_write(0x30, mvv_read(0x30)&0xFE); - mvv_write(0x08, mvv_read(0x08)|0x01); - mvv_write(0x01, mvv_read(0x01)&0xFD); - mvv_write(0x32, 0x00); - mvv_write(0x33, MVVMEMORYWIDTH); + mvv_write(dev, 0x30, mvv_read(dev, 0x30) & 0xfe); + mvv_write(dev, 0x08, mvv_read(dev, 0x08) | 0x01); + mvv_write(dev, 0x01, mvv_read(dev, 0x01) & 0xfd); + mvv_write(dev, 0x32, 0x00); + mvv_write(dev, 0x33, MVVMEMORYWIDTH); } @@ -621,52 +616,49 @@ static void pms_resolution(short width, short height) * Set Input */ -static void pms_vcrinput(short input) +static void pms_vcrinput(struct pms *dev, short input) { - if(decoder==PHILIPS2) - pms_i2c_andor(0x8A,0x0D,0x7F,(input&1)<<7); - else if(decoder==PHILIPS1) - pms_i2c_andor(0x42,0x0D,0x7F,(input&1)<<7); + if (dev->decoder == PHILIPS2) + pms_i2c_andor(dev, 0x8a, 0x0d, 0x7f, (input & 1) << 7); + else if (dev->decoder == PHILIPS1) + pms_i2c_andor(dev, 0x42, 0x0d, 0x7f, (input & 1) << 7); } -static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int count) +static int pms_capture(struct pms *dev, char __user *buf, int rgb555, int count) { int y; - int dw = 2*dev->width; - - char tmp[dw+32]; /* using a temp buffer is faster than direct */ + int dw = 2 * dev->width; + char tmp[dw + 32]; /* using a temp buffer is faster than direct */ int cnt = 0; - int len=0; + int len = 0; unsigned char r8 = 0x5; /* value for reg8 */ if (rgb555) r8 |= 0x20; /* else use untranslated rgb = 565 */ - mvv_write(0x08,r8); /* capture rgb555/565, init DRAM, PC enable */ + mvv_write(dev, 0x08, r8); /* capture rgb555/565, init DRAM, PC enable */ /* printf("%d %d %d %d %d %x %x\n",width,height,voff,nom,den,mvv_buf); */ - for (y = 0; y < dev->height; y++ ) - { - writeb(0, mem); /* synchronisiert neue Zeile */ + for (y = 0; y < dev->height; y++) { + writeb(0, dev->mem); /* synchronisiert neue Zeile */ /* * This is in truth a fifo, be very careful as if you * forgot this odd things will occur 8) */ - memcpy_fromio(tmp, mem, dw+32); /* discard 16 word */ + memcpy_fromio(tmp, dev->mem, dw + 32); /* discard 16 word */ cnt -= dev->height; - while (cnt <= 0) - { + while (cnt <= 0) { /* * Don't copy too far */ - int dt=dw; - if(dt+len>count) - dt=count-len; + int dt = dw; + if (dt + len > count) + dt = count - len; cnt += dev->height; - if (copy_to_user(buf, tmp+32, dt)) + if (copy_to_user(buf, tmp + 32, dt)) return len ? len : -EFAULT; buf += dt; len += dt; @@ -680,221 +672,278 @@ static int pms_capture(struct pms_device *dev, char __user *buf, int rgb555, int * Video4linux interfacing */ -static long pms_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int pms_querycap(struct file *file, void *priv, + struct v4l2_capability *vcap) { - struct video_device *dev = video_devdata(file); - struct pms_device *pd=(struct pms_device *)dev; - - switch(cmd) - { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - strcpy(b->name, "Mediavision PMS"); - b->type = VID_TYPE_CAPTURE|VID_TYPE_SCALES; - b->channels = 4; - b->audios = 0; - b->maxwidth = 640; - b->maxheight = 480; - b->minwidth = 16; - b->minheight = 16; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - if(v->channel<0 || v->channel>3) - return -EINVAL; - v->flags=0; - v->tuners=1; - /* Good question.. its composite or SVHS so.. */ - v->type = VIDEO_TYPE_CAMERA; - switch(v->channel) - { - case 0: - strcpy(v->name, "Composite");break; - case 1: - strcpy(v->name, "SVideo");break; - case 2: - strcpy(v->name, "Composite(VCR)");break; - case 3: - strcpy(v->name, "SVideo(VCR)");break; - } - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - if(v->channel<0 || v->channel>3) - return -EINVAL; - mutex_lock(&pd->lock); - pms_videosource(v->channel&1); - pms_vcrinput(v->channel>>1); - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCGTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - strcpy(v->name, "Format"); - v->rangelow=0; - v->rangehigh=0; - v->flags= VIDEO_TUNER_PAL|VIDEO_TUNER_NTSC|VIDEO_TUNER_SECAM; - switch(standard) - { - case 0: - v->mode = VIDEO_MODE_AUTO; - break; - case 1: - v->mode = VIDEO_MODE_NTSC; - break; - case 2: - v->mode = VIDEO_MODE_PAL; - break; - case 3: - v->mode = VIDEO_MODE_SECAM; - break; - } - return 0; - } - case VIDIOCSTUNER: - { - struct video_tuner *v = arg; - if(v->tuner) - return -EINVAL; - mutex_lock(&pd->lock); - switch(v->mode) - { - case VIDEO_MODE_AUTO: - pms_framerate(25); - pms_secamcross(0); - pms_format(0); - break; - case VIDEO_MODE_NTSC: - pms_framerate(30); - pms_secamcross(0); - pms_format(1); - break; - case VIDEO_MODE_PAL: - pms_framerate(25); - pms_secamcross(0); - pms_format(2); - break; - case VIDEO_MODE_SECAM: - pms_framerate(25); - pms_secamcross(1); - pms_format(2); - break; - default: - mutex_unlock(&pd->lock); - return -EINVAL; - } - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - *p = pd->picture; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - if(!((p->palette==VIDEO_PALETTE_RGB565 && p->depth==16) - ||(p->palette==VIDEO_PALETTE_RGB555 && p->depth==15))) - return -EINVAL; - pd->picture= *p; + struct pms *dev = video_drvdata(file); - /* - * Now load the card. - */ + strlcpy(vcap->driver, dev->v4l2_dev.name, sizeof(vcap->driver)); + strlcpy(vcap->card, "Mediavision PMS", sizeof(vcap->card)); + strlcpy(vcap->bus_info, "ISA", sizeof(vcap->bus_info)); + vcap->version = KERNEL_VERSION(0, 0, 3); + vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + return 0; +} - mutex_lock(&pd->lock); - pms_brightness(p->brightness>>8); - pms_hue(p->hue>>8); - pms_colour(p->colour>>8); - pms_contrast(p->contrast>>8); - mutex_unlock(&pd->lock); - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - if(vw->flags) - return -EINVAL; - if(vw->clipcount) - return -EINVAL; - if(vw->height<16||vw->height>480) - return -EINVAL; - if(vw->width<16||vw->width>640) - return -EINVAL; - pd->width=vw->width; - pd->height=vw->height; - mutex_lock(&pd->lock); - pms_resolution(pd->width, pd->height); - mutex_unlock(&pd->lock); /* Ok we figured out what to use from our wide choice */ - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - memset(vw,0,sizeof(*vw)); - vw->width=pd->width; - vw->height=pd->height; - return 0; - } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; +static int pms_enum_input(struct file *file, void *fh, struct v4l2_input *vin) +{ + static const char *inputs[4] = { + "Composite", + "S-Video", + "Composite (VCR)", + "S-Video (VCR)" + }; + + if (vin->index > 3) + return -EINVAL; + strlcpy(vin->name, inputs[vin->index], sizeof(vin->name)); + vin->type = V4L2_INPUT_TYPE_CAMERA; + vin->audioset = 0; + vin->tuner = 0; + vin->std = V4L2_STD_ALL; + vin->status = 0; + return 0; +} + +static int pms_g_input(struct file *file, void *fh, unsigned int *inp) +{ + struct pms *dev = video_drvdata(file); + + *inp = dev->input; + return 0; +} + +static int pms_s_input(struct file *file, void *fh, unsigned int inp) +{ + struct pms *dev = video_drvdata(file); + + if (inp > 3) + return -EINVAL; + + mutex_lock(&dev->lock); + dev->input = inp; + pms_videosource(dev, inp & 1); + pms_vcrinput(dev, inp >> 1); + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_g_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct pms *dev = video_drvdata(file); + + *std = dev->std; + return 0; +} + +static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + dev->std = *std; + mutex_lock(&dev->lock); + if (dev->std & V4L2_STD_NTSC) { + pms_framerate(dev, 30); + pms_secamcross(dev, 0); + pms_format(dev, 1); + } else if (dev->std & V4L2_STD_PAL) { + pms_framerate(dev, 25); + pms_secamcross(dev, 0); + pms_format(dev, 2); + } else if (dev->std & V4L2_STD_SECAM) { + pms_framerate(dev, 25); + pms_secamcross(dev, 1); + pms_format(dev, 2); + } else { + ret = -EINVAL; + } + /* + switch (v->mode) { + case VIDEO_MODE_AUTO: + pms_framerate(dev, 25); + pms_secamcross(dev, 0); + pms_format(dev, 0); + break; + }*/ + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 139); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 70); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 64); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0, 255, 1, 0); + } + return -EINVAL; +} + +static int pms_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dev->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dev->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dev->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dev->hue; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int pms_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct pms *dev = video_drvdata(file); + int ret = 0; + + mutex_lock(&dev->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + dev->brightness = ctrl->value; + pms_brightness(dev, dev->brightness); + break; + case V4L2_CID_CONTRAST: + dev->contrast = ctrl->value; + pms_contrast(dev, dev->contrast); + break; + case V4L2_CID_SATURATION: + dev->saturation = ctrl->value; + pms_saturation(dev, dev->saturation); + break; + case V4L2_CID_HUE: + dev->hue = ctrl->value; + pms_hue(dev, dev->hue); + break; + default: + ret = -EINVAL; + break; } + mutex_unlock(&dev->lock); + return ret; +} + +static int pms_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct pms *dev = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + pix->width = dev->width; + pix->height = dev->height; + pix->pixelformat = dev->width == 15 ? + V4L2_PIX_FMT_RGB555 : V4L2_PIX_FMT_RGB565; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * dev->width; + pix->sizeimage = 2 * dev->width * dev->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SRGB; return 0; } -static long pms_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) +static int pms_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { - return video_usercopy(file, cmd, arg, pms_do_ioctl); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + + if (pix->height < 16 || pix->height > 480) + return -EINVAL; + if (pix->width < 16 || pix->width > 640) + return -EINVAL; + if (pix->pixelformat != V4L2_PIX_FMT_RGB555 && + pix->pixelformat != V4L2_PIX_FMT_RGB565) + return -EINVAL; + pix->field = V4L2_FIELD_NONE; + pix->bytesperline = 2 * pix->width; + pix->sizeimage = 2 * pix->width * pix->height; + /* Just a guess */ + pix->colorspace = V4L2_COLORSPACE_SRGB; + return 0; +} + +static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) +{ + struct pms *dev = video_drvdata(file); + struct v4l2_pix_format *pix = &fmt->fmt.pix; + int ret = pms_try_fmt_vid_cap(file, fh, fmt); + + if (ret) + return ret; + mutex_lock(&dev->lock); + dev->width = pix->width; + dev->height = pix->height; + dev->depth = (pix->pixelformat == V4L2_PIX_FMT_RGB555) ? 15 : 16; + pms_resolution(dev, dev->width, dev->height); + /* Ok we figured out what to use from our wide choice */ + mutex_unlock(&dev->lock); + return 0; +} + +static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *fmt) +{ + static struct v4l2_fmtdesc formats[] = { + { 0, 0, 0, + "RGB 5:5:5", V4L2_PIX_FMT_RGB555, + { 0, 0, 0, 0 } + }, + { 0, 0, 0, + "RGB 5:6:5", V4L2_PIX_FMT_RGB565, + { 0, 0, 0, 0 } + }, + }; + enum v4l2_buf_type type = fmt->type; + + if (fmt->index > 1) + return -EINVAL; + + *fmt = formats[fmt->index]; + fmt->type = type; + return 0; } static ssize_t pms_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { - struct video_device *v = video_devdata(file); - struct pms_device *pd=(struct pms_device *)v; + struct pms *dev = video_drvdata(file); int len; - mutex_lock(&pd->lock); - len=pms_capture(pd, buf, (pd->picture.depth==16)?0:1,count); - mutex_unlock(&pd->lock); + mutex_lock(&dev->lock); + len = pms_capture(dev, buf, (dev->depth == 15), count); + mutex_unlock(&dev->lock); return len; } static int pms_exclusive_open(struct file *file) { - struct video_device *v = video_devdata(file); - struct pms_device *pd = (struct pms_device *)v; + struct pms *dev = video_drvdata(file); - return test_and_set_bit(0, &pd->in_use) ? -EBUSY : 0; + return test_and_set_bit(0, &dev->in_use) ? -EBUSY : 0; } static int pms_exclusive_release(struct file *file) { - struct video_device *v = video_devdata(file); - struct pms_device *pd = (struct pms_device *)v; + struct pms *dev = video_drvdata(file); - clear_bit(0, &pd->in_use); + clear_bit(0, &dev->in_use); return 0; } @@ -902,78 +951,81 @@ static const struct v4l2_file_operations pms_fops = { .owner = THIS_MODULE, .open = pms_exclusive_open, .release = pms_exclusive_release, - .ioctl = pms_ioctl, + .ioctl = video_ioctl2, .read = pms_read, }; -static struct video_device pms_template= -{ - .name = "Mediavision PMS", - .fops = &pms_fops, - .release = video_device_release_empty, +static const struct v4l2_ioctl_ops pms_ioctl_ops = { + .vidioc_querycap = pms_querycap, + .vidioc_g_input = pms_g_input, + .vidioc_s_input = pms_s_input, + .vidioc_enum_input = pms_enum_input, + .vidioc_g_std = pms_g_std, + .vidioc_s_std = pms_s_std, + .vidioc_queryctrl = pms_queryctrl, + .vidioc_g_ctrl = pms_g_ctrl, + .vidioc_s_ctrl = pms_s_ctrl, + .vidioc_enum_fmt_vid_cap = pms_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pms_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pms_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pms_try_fmt_vid_cap, }; -static struct pms_device pms_device; - - /* * Probe for and initialise the Mediavision PMS */ -static int init_mediavision(void) +static int init_mediavision(struct pms *dev) { int id; int idec, decst; int i; - - unsigned char i2c_defs[]={ - 0x4C,0x30,0x00,0xE8, - 0xB6,0xE2,0x00,0x00, - 0xFF,0xFF,0x00,0x00, - 0x00,0x00,0x78,0x98, - 0x00,0x00,0x00,0x00, - 0x34,0x0A,0xF4,0xCE, - 0xE4 + static const unsigned char i2c_defs[] = { + 0x4c, 0x30, 0x00, 0xe8, + 0xb6, 0xe2, 0x00, 0x00, + 0xff, 0xff, 0x00, 0x00, + 0x00, 0x00, 0x78, 0x98, + 0x00, 0x00, 0x00, 0x00, + 0x34, 0x0a, 0xf4, 0xce, + 0xe4 }; - mem = ioremap(mem_base, 0x800); - if (!mem) + dev->mem = ioremap(mem_base, 0x800); + if (!dev->mem) return -ENOMEM; - if (!request_region(0x9A01, 1, "Mediavision PMS config")) - { - printk(KERN_WARNING "mediavision: unable to detect: 0x9A01 in use.\n"); - iounmap(mem); + if (!request_region(0x9a01, 1, "Mediavision PMS config")) { + printk(KERN_WARNING "mediavision: unable to detect: 0x9a01 in use.\n"); + iounmap(dev->mem); return -EBUSY; } - if (!request_region(io_port, 3, "Mediavision PMS")) - { - printk(KERN_WARNING "mediavision: I/O port %d in use.\n", io_port); - release_region(0x9A01, 1); - iounmap(mem); + if (!request_region(dev->io, 3, "Mediavision PMS")) { + printk(KERN_WARNING "mediavision: I/O port %d in use.\n", dev->io); + release_region(0x9a01, 1); + iounmap(dev->mem); return -EBUSY; } - outb(0xB8, 0x9A01); /* Unlock */ - outb(io_port>>4, 0x9A01); /* Set IO port */ + outb(0xb8, 0x9a01); /* Unlock */ + outb(dev->io >> 4, 0x9a01); /* Set IO port */ - id=mvv_read(3); - decst=pms_i2c_stat(0x43); + id = mvv_read(dev, 3); + decst = pms_i2c_stat(dev, 0x43); - if(decst!=-1) - idec=2; - else if(pms_i2c_stat(0xb9)!=-1) - idec=3; - else if(pms_i2c_stat(0x8b)!=-1) - idec=1; + if (decst != -1) + idec = 2; + else if (pms_i2c_stat(dev, 0xb9) != -1) + idec = 3; + else if (pms_i2c_stat(dev, 0x8b) != -1) + idec = 1; else - idec=0; + idec = 0; printk(KERN_INFO "PMS type is %d\n", idec); - if(idec == 0) { - release_region(io_port, 3); - release_region(0x9A01, 1); - iounmap(mem); + if (idec == 0) { + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); return -ENODEV; } @@ -981,51 +1033,50 @@ static int init_mediavision(void) * Ok we have a PMS of some sort */ - mvv_write(0x04, mem_base>>12); /* Set the memory area */ + mvv_write(dev, 0x04, mem_base >> 12); /* Set the memory area */ /* Ok now load the defaults */ - for(i=0;i<0x19;i++) - { - if(i2c_defs[i]==0xFF) - pms_i2c_andor(0x8A, i, 0x07,0x00); + for (i = 0; i < 0x19; i++) { + if (i2c_defs[i] == 0xff) + pms_i2c_andor(dev, 0x8a, i, 0x07, 0x00); else - pms_i2c_write(0x8A, i, i2c_defs[i]); + pms_i2c_write(dev, 0x8a, i, i2c_defs[i]); } - pms_i2c_write(0xB8,0x00,0x12); - pms_i2c_write(0xB8,0x04,0x00); - pms_i2c_write(0xB8,0x07,0x00); - pms_i2c_write(0xB8,0x08,0x00); - pms_i2c_write(0xB8,0x09,0xFF); - pms_i2c_write(0xB8,0x0A,0x00); - pms_i2c_write(0xB8,0x0B,0x10); - pms_i2c_write(0xB8,0x10,0x03); - - mvv_write(0x01, 0x00); - mvv_write(0x05, 0xA0); - mvv_write(0x08, 0x25); - mvv_write(0x09, 0x00); - mvv_write(0x0A, 0x20|MVVMEMORYWIDTH); - - mvv_write(0x10, 0x02); - mvv_write(0x1E, 0x0C); - mvv_write(0x1F, 0x03); - mvv_write(0x26, 0x06); - - mvv_write(0x2B, 0x00); - mvv_write(0x2C, 0x20); - mvv_write(0x2D, 0x00); - mvv_write(0x2F, 0x70); - mvv_write(0x32, 0x00); - mvv_write(0x33, MVVMEMORYWIDTH); - mvv_write(0x34, 0x00); - mvv_write(0x35, 0x00); - mvv_write(0x3A, 0x80); - mvv_write(0x3B, 0x10); - mvv_write(0x20, 0x00); - mvv_write(0x21, 0x00); - mvv_write(0x30, 0x22); + pms_i2c_write(dev, 0xb8, 0x00, 0x12); + pms_i2c_write(dev, 0xb8, 0x04, 0x00); + pms_i2c_write(dev, 0xb8, 0x07, 0x00); + pms_i2c_write(dev, 0xb8, 0x08, 0x00); + pms_i2c_write(dev, 0xb8, 0x09, 0xff); + pms_i2c_write(dev, 0xb8, 0x0a, 0x00); + pms_i2c_write(dev, 0xb8, 0x0b, 0x10); + pms_i2c_write(dev, 0xb8, 0x10, 0x03); + + mvv_write(dev, 0x01, 0x00); + mvv_write(dev, 0x05, 0xa0); + mvv_write(dev, 0x08, 0x25); + mvv_write(dev, 0x09, 0x00); + mvv_write(dev, 0x0a, 0x20 | MVVMEMORYWIDTH); + + mvv_write(dev, 0x10, 0x02); + mvv_write(dev, 0x1e, 0x0c); + mvv_write(dev, 0x1f, 0x03); + mvv_write(dev, 0x26, 0x06); + + mvv_write(dev, 0x2b, 0x00); + mvv_write(dev, 0x2c, 0x20); + mvv_write(dev, 0x2d, 0x00); + mvv_write(dev, 0x2f, 0x70); + mvv_write(dev, 0x32, 0x00); + mvv_write(dev, 0x33, MVVMEMORYWIDTH); + mvv_write(dev, 0x34, 0x00); + mvv_write(dev, 0x35, 0x00); + mvv_write(dev, 0x3a, 0x80); + mvv_write(dev, 0x3b, 0x10); + mvv_write(dev, 0x20, 0x00); + mvv_write(dev, 0x21, 0x00); + mvv_write(dev, 0x30, 0x22); return 0; } @@ -1038,53 +1089,77 @@ static int enable; module_param(enable, int, 0); #endif -static int __init init_pms_cards(void) +static int __init pms_init(void) { - printk(KERN_INFO "Mediavision Pro Movie Studio driver 0.02\n"); + struct pms *dev = &pms_card; + struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + int res; + + strlcpy(v4l2_dev->name, "pms", sizeof(v4l2_dev->name)); + + v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.03\n"); #ifndef MODULE if (!enable) { - printk(KERN_INFO "PMS: not enabled, use pms.enable=1 to " - "probe\n"); + v4l2_err(v4l2_dev, + "PMS: not enabled, use pms.enable=1 to probe\n"); return -ENODEV; } #endif - data_port = io_port +1; + dev->decoder = PHILIPS2; + dev->io = io_port; + dev->data = io_port + 1; - if(init_mediavision()) - { - printk(KERN_INFO "Board not found.\n"); + if (init_mediavision(dev)) { + v4l2_err(v4l2_dev, "Board not found.\n"); return -ENODEV; } - memcpy(&pms_device, &pms_template, sizeof(pms_template)); - mutex_init(&pms_device.lock); - pms_device.height=240; - pms_device.width=320; - pms_swsense(75); - pms_resolution(320,240); - return video_register_device((struct video_device *)&pms_device, VFL_TYPE_GRABBER, video_nr); -} - -module_param(io_port, int, 0); -module_param(mem_base, int, 0); -module_param(video_nr, int, 0); -MODULE_LICENSE("GPL"); + res = v4l2_device_register(NULL, v4l2_dev); + if (res < 0) { + v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); + return res; + } -static void __exit shutdown_mediavision(void) -{ - release_region(io_port,3); - release_region(0x9A01, 1); + strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); + dev->vdev.v4l2_dev = v4l2_dev; + dev->vdev.fops = &pms_fops; + dev->vdev.ioctl_ops = &pms_ioctl_ops; + dev->vdev.release = video_device_release_empty; + video_set_drvdata(&dev->vdev, dev); + mutex_init(&dev->lock); + dev->std = V4L2_STD_NTSC_M; + dev->height = 240; + dev->width = 320; + dev->depth = 15; + dev->brightness = 139; + dev->contrast = 70; + dev->hue = 0; + dev->saturation = 64; + pms_swsense(dev, 75); + pms_resolution(dev, 320, 240); + pms_videosource(dev, 0); + pms_vcrinput(dev, 0); + if (video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + v4l2_device_unregister(&dev->v4l2_dev); + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); + return -EINVAL; + } + return 0; } -static void __exit cleanup_pms_module(void) +static void __exit pms_exit(void) { - shutdown_mediavision(); - video_unregister_device((struct video_device *)&pms_device); - iounmap(mem); -} + struct pms *dev = &pms_card; -module_init(init_pms_cards); -module_exit(cleanup_pms_module); + video_unregister_device(&dev->vdev); + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); +} +module_init(pms_init); +module_exit(pms_exit); diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c index fbe3856bdca65..ae977668c4969 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c +++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c @@ -142,6 +142,9 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt) { int bcnt = 0; int ccnt; + ccnt = scnprintf(buf, acnt, "Driver hardware description: %s\n", + pvr2_hdw_get_desc(hdw)); + bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = scnprintf(buf,acnt,"Driver state info:\n"); bcnt += ccnt; acnt -= ccnt; buf += ccnt; ccnt = pvr2_hdw_state_report(hdw,buf,acnt); @@ -249,11 +252,15 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf, scnt = debugifc_isolate_word(buf,count,&wptr,&wlen); if (scnt && wptr) { count -= scnt; buf += scnt; - if (debugifc_match_keyword(wptr,wlen,"prom")) { - pvr2_hdw_cpufw_set_enabled(hdw,!0,!0); - } else if (debugifc_match_keyword(wptr,wlen, - "ram")) { - pvr2_hdw_cpufw_set_enabled(hdw,0,!0); + if (debugifc_match_keyword(wptr, wlen, + "prom")) { + pvr2_hdw_cpufw_set_enabled(hdw, 2, !0); + } else if (debugifc_match_keyword(wptr, wlen, + "ram8k")) { + pvr2_hdw_cpufw_set_enabled(hdw, 0, !0); + } else if (debugifc_match_keyword(wptr, wlen, + "ram16k")) { + pvr2_hdw_cpufw_set_enabled(hdw, 1, !0); } else { return -EINVAL; } diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c index e4d7c13cab879..6bc16c13ccef7 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -58,7 +58,7 @@ static const char *pvr2_fw1_names_29xxx[] = { }; static const struct pvr2_device_desc pvr2_device_29xxx = { - .description = "WinTV PVR USB2 Model Category 29xxx", + .description = "WinTV PVR USB2 Model 29xxx", .shortname = "29xxx", .client_table.lst = pvr2_cli_29xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx), @@ -91,7 +91,7 @@ static const char *pvr2_fw1_names_24xxx[] = { }; static const struct pvr2_device_desc pvr2_device_24xxx = { - .description = "WinTV PVR USB2 Model Category 24xxx", + .description = "WinTV PVR USB2 Model 24xxx", .shortname = "24xxx", .client_table.lst = pvr2_cli_24xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx), @@ -340,7 +340,7 @@ static const char *pvr2_fw1_names_73xxx[] = { }; static const struct pvr2_device_desc pvr2_device_73xxx = { - .description = "WinTV HVR-1900 Model Category 73xxx", + .description = "WinTV HVR-1900 Model 73xxx", .shortname = "73xxx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -351,6 +351,7 @@ static const struct pvr2_device_desc pvr2_device_73xxx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE, @@ -445,7 +446,7 @@ static const char *pvr2_fw1_names_75xxx[] = { }; static const struct pvr2_device_desc pvr2_device_750xx = { - .description = "WinTV HVR-1950 Model Category 750xx", + .description = "WinTV HVR-1950 Model 750xx", .shortname = "750xx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -456,6 +457,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, @@ -467,7 +469,7 @@ static const struct pvr2_device_desc pvr2_device_750xx = { }; static const struct pvr2_device_desc pvr2_device_751xx = { - .description = "WinTV HVR-1950 Model Category 751xx", + .description = "WinTV HVR-1950 Model 751xx", .shortname = "751xx", .client_table.lst = pvr2_cli_73xxx, .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx), @@ -478,6 +480,7 @@ static const struct pvr2_device_desc pvr2_device_751xx = { .flag_has_analogtuner = !0, .flag_has_composite = !0, .flag_has_svideo = !0, + .flag_fx2_16kb = !0, .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE, .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE, .default_std_mask = V4L2_STD_NTSC_M, diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h index ea04ecf8aa39a..e5b9594eb5f62 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h +++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h @@ -176,6 +176,7 @@ struct pvr2_device_desc { unsigned int flag_has_analogtuner:1; /* Has analog tuner */ unsigned int flag_has_composite:1; /* Has composite input */ unsigned int flag_has_svideo:1; /* Has s-video input */ + unsigned int flag_fx2_16kb:1; /* 16KB FX2 firmware OK here */ }; extern struct usb_device_id pvr2_device_table[]; diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c index 54ac5349dee2f..e046fdaec5ae2 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c +++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c @@ -294,7 +294,10 @@ static int pvr2_encoder_cmd(void *ctxt, pvr2_trace( PVR2_TRACE_ERROR_LEGS, "Giving up on command." - " This is normally recovered by the driver."); + " This is normally recovered via a firmware" + " reload and re-initialization; concern" + " is only warranted if this happens repeatedly" + " and rapidly."); break; } wrData[0] = 0x7; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 5fcad28211d2b..de5485f506b1b 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -270,6 +270,7 @@ struct pvr2_hdw { int force_dirty; /* consider all controls dirty if true */ int flag_ok; /* device in known good state */ + int flag_modulefail; /* true if at least one module failed to load */ int flag_disconnected; /* flag_ok == 0 due to disconnect */ int flag_init_ok; /* true if structure is fully initialized */ int fw1_state; /* current situation with fw1 */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index 13639b302700f..1bbdab08fe0e6 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -1447,6 +1447,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) const struct firmware *fw_entry = NULL; void *fw_ptr; unsigned int pipe; + unsigned int fwsize; int ret; u16 address; @@ -1473,9 +1474,21 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f)); pipe = usb_sndctrlpipe(hdw->usb_dev, 0); + fwsize = fw_entry->size; - if (fw_entry->size != 0x2000){ - pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size"); + if ((fwsize != 0x2000) && + (!(hdw->hdw_desc->flag_fx2_16kb && (fwsize == 0x4000)))) { + if (hdw->hdw_desc->flag_fx2_16kb) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Wrong fx2 firmware size" + " (expected 8192 or 16384, got %u)", + fwsize); + } else { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "Wrong fx2 firmware size" + " (expected 8192, got %u)", + fwsize); + } release_firmware(fw_entry); return -ENOMEM; } @@ -1493,7 +1506,7 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) chunk. */ ret = 0; - for(address = 0; address < fw_entry->size; address += 0x800) { + for (address = 0; address < fwsize; address += 0x800) { memcpy(fw_ptr, fw_entry->data + address, 0x800); ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address, 0, fw_ptr, 0x800, HZ); @@ -1509,8 +1522,8 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw) trace_firmware("Upload done (%d bytes sent)",ret); - /* We should have written 8192 bytes */ - if (ret == 8192) { + /* We should have written fwsize bytes */ + if (ret == fwsize) { hdw->fw1_state = FW1_STATE_RELOAD; return 0; } @@ -2030,7 +2043,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL; if (!fname) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u for device %s has no name", + "Module ID %u for device %s has no name?" + " The driver might have a configuration problem.", mid, hdw->hdw_desc->description); return -EINVAL; @@ -2058,7 +2072,8 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, if (!i2ccnt) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, "Module ID %u (%s) for device %s:" - " No i2c addresses", + " No i2c addresses." + " The driver might have a configuration problem.", mid, fname, hdw->hdw_desc->description); return -EINVAL; } @@ -2090,7 +2105,9 @@ static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw, if (!sd) { pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Module ID %u (%s) for device %s failed to load", + "Module ID %u (%s) for device %s failed to load." + " Possible missing sub-device kernel module or" + " initialization failure within module.", mid, fname, hdw->hdw_desc->description); return -EIO; } @@ -2132,7 +2149,10 @@ static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw) for (idx = 0; idx < ct->cnt; idx++) { if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0; } - if (!okFl) pvr2_hdw_render_useless(hdw); + if (!okFl) { + hdw->flag_modulefail = !0; + pvr2_hdw_render_useless(hdw); + } } @@ -2334,6 +2354,20 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw) break; } } + if (hdw->flag_modulefail) { + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "***WARNING*** pvrusb2 driver initialization" + " failed due to the failure of one or more" + " sub-device kernel modules."); + pvr2_trace( + PVR2_TRACE_ERROR_LEGS, + "You need to resolve the failing condition" + " before this driver can function. There" + " should be some earlier messages giving more" + " information about the problem."); + break; + } if (procreload) { pvr2_trace( PVR2_TRACE_ERROR_LEGS, @@ -2419,6 +2453,8 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, hdw = kzalloc(sizeof(*hdw),GFP_KERNEL); pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"", hdw,hdw_desc->description); + pvr2_trace(PVR2_TRACE_INFO, "Hardware description: %s", + hdw_desc->description); if (!hdw) goto fail; init_timer(&hdw->quiescent_timer); @@ -3480,7 +3516,7 @@ static u8 *pvr2_full_eeprom_fetch(struct pvr2_hdw *hdw) void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, - int prom_flag, + int mode, int enable_flag) { int ret; @@ -3503,11 +3539,12 @@ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, break; } - hdw->fw_cpu_flag = (prom_flag == 0); + hdw->fw_cpu_flag = (mode != 2); if (hdw->fw_cpu_flag) { + hdw->fw_size = (mode == 1) ? 0x4000 : 0x2000; pvr2_trace(PVR2_TRACE_FIRMWARE, - "Preparing to suck out CPU firmware"); - hdw->fw_size = 0x2000; + "Preparing to suck out CPU firmware" + " (size=%u)", hdw->fw_size); hdw->fw_buffer = kzalloc(hdw->fw_size,GFP_KERNEL); if (!hdw->fw_buffer) { hdw->fw_size = 0; diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 7b6940554e9a9..56e70eae20c12 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -219,7 +219,7 @@ int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, this may prevent the device from running (and leaving this mode may imply a device reset). */ void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, - int prom_flag, + int mode, /* 0=8KB FX2, 1=16KB FX2, 2=PROM */ int enable_flag); /* Return true if we're in a mode for retrieval CPU firmware */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c index a334b1a966a2a..7cbe18c4ca956 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c +++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c @@ -50,6 +50,7 @@ MODULE_PARM_DESC(disable_autoload_ir_video, /* Mapping of IR schemes to known I2C addresses - if any */ static const unsigned char ir_video_addresses[] = { + [PVR2_IR_SCHEME_ZILOG] = 0x71, [PVR2_IR_SCHEME_29XXX] = 0x18, [PVR2_IR_SCHEME_24XXX] = 0x18, }; diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index 2d8825e5b1bef..6aa48e0ae7312 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -913,6 +913,15 @@ static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) } +static void pvr2_v4l2_dev_disassociate_parent(struct pvr2_v4l2_dev *dip) +{ + if (!dip) return; + if (!dip->devbase.parent) return; + dip->devbase.parent = NULL; + device_move(&dip->devbase.dev, NULL, DPM_ORDER_NONE); +} + + static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp) { if (vp->dev_video) { @@ -943,6 +952,8 @@ static void pvr2_v4l2_internal_check(struct pvr2_channel *chp) struct pvr2_v4l2 *vp; vp = container_of(chp,struct pvr2_v4l2,channel); if (!vp->channel.mc_head->disconnect_flag) return; + pvr2_v4l2_dev_disassociate_parent(vp->dev_video); + pvr2_v4l2_dev_disassociate_parent(vp->dev_radio); if (vp->vfirst) return; pvr2_v4l2_destroy_no_lock(vp); } @@ -1250,12 +1261,13 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct pvr2_v4l2 *vp, int v4l_type) { + struct usb_device *usbdev; int mindevnum; int unit_number; int *nr_ptr = NULL; dip->v4lp = vp; - + usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw); dip->v4l_type = v4l_type; switch (v4l_type) { case VFL_TYPE_GRABBER: @@ -1296,6 +1308,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { mindevnum = nr_ptr[unit_number]; } + dip->devbase.parent = &usbdev->dev; if ((video_register_device(&dip->devbase, dip->v4l_type, mindevnum) < 0) && (video_register_device(&dip->devbase, diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index f976df452a341..89b620f6db7b4 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -68,6 +68,7 @@ #endif #include #include +#include /* simple_strtol() */ #include "pwc.h" #include "pwc-kiara.h" @@ -1916,19 +1917,6 @@ static void usb_pwc_disconnect(struct usb_interface *intf) unlock_kernel(); } -/* *grunt* We have to do atoi ourselves :-( */ -static int pwc_atoi(const char *s) -{ - int k = 0; - - k = 0; - while (*s != '\0' && *s >= '0' && *s <= '9') { - k = 10 * k + (*s - '0'); - s++; - } - return k; -} - /* * Initialization code & module stuff @@ -2078,13 +2066,16 @@ static int __init usb_pwc_init(void) } else { /* No type or serial number specified, just a number. */ - device_hint[i].device_node = pwc_atoi(s); + device_hint[i].device_node = + simple_strtol(s, NULL, 10); } } else { /* There's a colon, so we have at least a type and a device node */ - device_hint[i].type = pwc_atoi(s); - device_hint[i].device_node = pwc_atoi(colon + 1); + device_hint[i].type = + simple_strtol(s, NULL, 10); + device_hint[i].device_node = + simple_strtol(colon + 1, NULL, 10); if (*dot != '\0') { /* There's a serial number as well */ int k; diff --git a/drivers/media/video/rj54n1cb0c.c b/drivers/media/video/rj54n1cb0c.c new file mode 100644 index 0000000000000..373f2a30a677b --- /dev/null +++ b/drivers/media/video/rj54n1cb0c.c @@ -0,0 +1,1219 @@ +/* + * Driver for RJ54N1CB0C CMOS Image Sensor from Micron + * + * Copyright (C) 2009, Guennadi Liakhovetski + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#define RJ54N1_DEV_CODE 0x0400 +#define RJ54N1_DEV_CODE2 0x0401 +#define RJ54N1_OUT_SEL 0x0403 +#define RJ54N1_XY_OUTPUT_SIZE_S_H 0x0404 +#define RJ54N1_X_OUTPUT_SIZE_S_L 0x0405 +#define RJ54N1_Y_OUTPUT_SIZE_S_L 0x0406 +#define RJ54N1_XY_OUTPUT_SIZE_P_H 0x0407 +#define RJ54N1_X_OUTPUT_SIZE_P_L 0x0408 +#define RJ54N1_Y_OUTPUT_SIZE_P_L 0x0409 +#define RJ54N1_LINE_LENGTH_PCK_S_H 0x040a +#define RJ54N1_LINE_LENGTH_PCK_S_L 0x040b +#define RJ54N1_LINE_LENGTH_PCK_P_H 0x040c +#define RJ54N1_LINE_LENGTH_PCK_P_L 0x040d +#define RJ54N1_RESIZE_N 0x040e +#define RJ54N1_RESIZE_N_STEP 0x040f +#define RJ54N1_RESIZE_STEP 0x0410 +#define RJ54N1_RESIZE_HOLD_H 0x0411 +#define RJ54N1_RESIZE_HOLD_L 0x0412 +#define RJ54N1_H_OBEN_OFS 0x0413 +#define RJ54N1_V_OBEN_OFS 0x0414 +#define RJ54N1_RESIZE_CONTROL 0x0415 +#define RJ54N1_INC_USE_SEL_H 0x0425 +#define RJ54N1_INC_USE_SEL_L 0x0426 +#define RJ54N1_MIRROR_STILL_MODE 0x0427 +#define RJ54N1_INIT_START 0x0428 +#define RJ54N1_SCALE_1_2_LEV 0x0429 +#define RJ54N1_SCALE_4_LEV 0x042a +#define RJ54N1_Y_GAIN 0x04d8 +#define RJ54N1_APT_GAIN_UP 0x04fa +#define RJ54N1_RA_SEL_UL 0x0530 +#define RJ54N1_BYTE_SWAP 0x0531 +#define RJ54N1_OUT_SIGPO 0x053b +#define RJ54N1_FRAME_LENGTH_S_H 0x0595 +#define RJ54N1_FRAME_LENGTH_S_L 0x0596 +#define RJ54N1_FRAME_LENGTH_P_H 0x0597 +#define RJ54N1_FRAME_LENGTH_P_L 0x0598 +#define RJ54N1_IOC 0x05ef +#define RJ54N1_TG_BYPASS 0x0700 +#define RJ54N1_PLL_L 0x0701 +#define RJ54N1_PLL_N 0x0702 +#define RJ54N1_PLL_EN 0x0704 +#define RJ54N1_RATIO_TG 0x0706 +#define RJ54N1_RATIO_T 0x0707 +#define RJ54N1_RATIO_R 0x0708 +#define RJ54N1_RAMP_TGCLK_EN 0x0709 +#define RJ54N1_OCLK_DSP 0x0710 +#define RJ54N1_RATIO_OP 0x0711 +#define RJ54N1_RATIO_O 0x0712 +#define RJ54N1_OCLK_SEL_EN 0x0713 +#define RJ54N1_CLK_RST 0x0717 +#define RJ54N1_RESET_STANDBY 0x0718 + +#define E_EXCLK (1 << 7) +#define SOFT_STDBY (1 << 4) +#define SEN_RSTX (1 << 2) +#define TG_RSTX (1 << 1) +#define DSP_RSTX (1 << 0) + +#define RESIZE_HOLD_SEL (1 << 2) +#define RESIZE_GO (1 << 1) + +#define RJ54N1_COLUMN_SKIP 0 +#define RJ54N1_ROW_SKIP 0 +#define RJ54N1_MAX_WIDTH 1600 +#define RJ54N1_MAX_HEIGHT 1200 + +/* I2C addresses: 0x50, 0x51, 0x60, 0x61 */ + +static const struct soc_camera_data_format rj54n1_colour_formats[] = { + { + .name = "YUYV", + .depth = 16, + .fourcc = V4L2_PIX_FMT_YUYV, + .colorspace = V4L2_COLORSPACE_JPEG, + }, { + .name = "RGB565", + .depth = 16, + .fourcc = V4L2_PIX_FMT_RGB565, + .colorspace = V4L2_COLORSPACE_SRGB, + } +}; + +struct rj54n1_clock_div { + u8 ratio_tg; + u8 ratio_t; + u8 ratio_r; + u8 ratio_op; + u8 ratio_o; +}; + +struct rj54n1 { + struct v4l2_subdev subdev; + struct v4l2_rect rect; /* Sensor window */ + unsigned short width; /* Output window */ + unsigned short height; + unsigned short resize; /* Sensor * 1024 / resize = Output */ + struct rj54n1_clock_div clk_div; + u32 fourcc; + unsigned short scale; + u8 bank; +}; + +struct rj54n1_reg_val { + u16 reg; + u8 val; +}; + +const static struct rj54n1_reg_val bank_4[] = { + {0x417, 0}, + {0x42c, 0}, + {0x42d, 0xf0}, + {0x42e, 0}, + {0x42f, 0x50}, + {0x430, 0xf5}, + {0x431, 0x16}, + {0x432, 0x20}, + {0x433, 0}, + {0x434, 0xc8}, + {0x43c, 8}, + {0x43e, 0x90}, + {0x445, 0x83}, + {0x4ba, 0x58}, + {0x4bb, 4}, + {0x4bc, 0x20}, + {0x4db, 4}, + {0x4fe, 2}, +}; + +const static struct rj54n1_reg_val bank_5[] = { + {0x514, 0}, + {0x516, 0}, + {0x518, 0}, + {0x51a, 0}, + {0x51d, 0xff}, + {0x56f, 0x28}, + {0x575, 0x40}, + {0x5bc, 0x48}, + {0x5c1, 6}, + {0x5e5, 0x11}, + {0x5e6, 0x43}, + {0x5e7, 0x33}, + {0x5e8, 0x21}, + {0x5e9, 0x30}, + {0x5ea, 0x0}, + {0x5eb, 0xa5}, + {0x5ec, 0xff}, + {0x5fe, 2}, +}; + +const static struct rj54n1_reg_val bank_7[] = { + {0x70a, 0}, + {0x714, 0xff}, + {0x715, 0xff}, + {0x716, 0x1f}, + {0x7FE, 0x02}, +}; + +const static struct rj54n1_reg_val bank_8[] = { + {0x800, 0x00}, + {0x801, 0x01}, + {0x802, 0x61}, + {0x805, 0x00}, + {0x806, 0x00}, + {0x807, 0x00}, + {0x808, 0x00}, + {0x809, 0x01}, + {0x80A, 0x61}, + {0x80B, 0x00}, + {0x80C, 0x01}, + {0x80D, 0x00}, + {0x80E, 0x00}, + {0x80F, 0x00}, + {0x810, 0x00}, + {0x811, 0x01}, + {0x812, 0x61}, + {0x813, 0x00}, + {0x814, 0x11}, + {0x815, 0x00}, + {0x816, 0x41}, + {0x817, 0x00}, + {0x818, 0x51}, + {0x819, 0x01}, + {0x81A, 0x1F}, + {0x81B, 0x00}, + {0x81C, 0x01}, + {0x81D, 0x00}, + {0x81E, 0x11}, + {0x81F, 0x00}, + {0x820, 0x41}, + {0x821, 0x00}, + {0x822, 0x51}, + {0x823, 0x00}, + {0x824, 0x00}, + {0x825, 0x00}, + {0x826, 0x47}, + {0x827, 0x01}, + {0x828, 0x4F}, + {0x829, 0x00}, + {0x82A, 0x00}, + {0x82B, 0x00}, + {0x82C, 0x30}, + {0x82D, 0x00}, + {0x82E, 0x40}, + {0x82F, 0x00}, + {0x830, 0xB3}, + {0x831, 0x00}, + {0x832, 0xE3}, + {0x833, 0x00}, + {0x834, 0x00}, + {0x835, 0x00}, + {0x836, 0x00}, + {0x837, 0x00}, + {0x838, 0x00}, + {0x839, 0x01}, + {0x83A, 0x61}, + {0x83B, 0x00}, + {0x83C, 0x01}, + {0x83D, 0x00}, + {0x83E, 0x00}, + {0x83F, 0x00}, + {0x840, 0x00}, + {0x841, 0x01}, + {0x842, 0x61}, + {0x843, 0x00}, + {0x844, 0x1D}, + {0x845, 0x00}, + {0x846, 0x00}, + {0x847, 0x00}, + {0x848, 0x00}, + {0x849, 0x01}, + {0x84A, 0x1F}, + {0x84B, 0x00}, + {0x84C, 0x05}, + {0x84D, 0x00}, + {0x84E, 0x19}, + {0x84F, 0x01}, + {0x850, 0x21}, + {0x851, 0x01}, + {0x852, 0x5D}, + {0x853, 0x00}, + {0x854, 0x00}, + {0x855, 0x00}, + {0x856, 0x19}, + {0x857, 0x01}, + {0x858, 0x21}, + {0x859, 0x00}, + {0x85A, 0x00}, + {0x85B, 0x00}, + {0x85C, 0x00}, + {0x85D, 0x00}, + {0x85E, 0x00}, + {0x85F, 0x00}, + {0x860, 0xB3}, + {0x861, 0x00}, + {0x862, 0xE3}, + {0x863, 0x00}, + {0x864, 0x00}, + {0x865, 0x00}, + {0x866, 0x00}, + {0x867, 0x00}, + {0x868, 0x00}, + {0x869, 0xE2}, + {0x86A, 0x00}, + {0x86B, 0x01}, + {0x86C, 0x06}, + {0x86D, 0x00}, + {0x86E, 0x00}, + {0x86F, 0x00}, + {0x870, 0x60}, + {0x871, 0x8C}, + {0x872, 0x10}, + {0x873, 0x00}, + {0x874, 0xE0}, + {0x875, 0x00}, + {0x876, 0x27}, + {0x877, 0x01}, + {0x878, 0x00}, + {0x879, 0x00}, + {0x87A, 0x00}, + {0x87B, 0x03}, + {0x87C, 0x00}, + {0x87D, 0x00}, + {0x87E, 0x00}, + {0x87F, 0x00}, + {0x880, 0x00}, + {0x881, 0x00}, + {0x882, 0x00}, + {0x883, 0x00}, + {0x884, 0x00}, + {0x885, 0x00}, + {0x886, 0xF8}, + {0x887, 0x00}, + {0x888, 0x03}, + {0x889, 0x00}, + {0x88A, 0x64}, + {0x88B, 0x00}, + {0x88C, 0x03}, + {0x88D, 0x00}, + {0x88E, 0xB1}, + {0x88F, 0x00}, + {0x890, 0x03}, + {0x891, 0x01}, + {0x892, 0x1D}, + {0x893, 0x00}, + {0x894, 0x03}, + {0x895, 0x01}, + {0x896, 0x4B}, + {0x897, 0x00}, + {0x898, 0xE5}, + {0x899, 0x00}, + {0x89A, 0x01}, + {0x89B, 0x00}, + {0x89C, 0x01}, + {0x89D, 0x04}, + {0x89E, 0xC8}, + {0x89F, 0x00}, + {0x8A0, 0x01}, + {0x8A1, 0x01}, + {0x8A2, 0x61}, + {0x8A3, 0x00}, + {0x8A4, 0x01}, + {0x8A5, 0x00}, + {0x8A6, 0x00}, + {0x8A7, 0x00}, + {0x8A8, 0x00}, + {0x8A9, 0x00}, + {0x8AA, 0x7F}, + {0x8AB, 0x03}, + {0x8AC, 0x00}, + {0x8AD, 0x00}, + {0x8AE, 0x00}, + {0x8AF, 0x00}, + {0x8B0, 0x00}, + {0x8B1, 0x00}, + {0x8B6, 0x00}, + {0x8B7, 0x01}, + {0x8B8, 0x00}, + {0x8B9, 0x00}, + {0x8BA, 0x02}, + {0x8BB, 0x00}, + {0x8BC, 0xFF}, + {0x8BD, 0x00}, + {0x8FE, 0x02}, +}; + +const static struct rj54n1_reg_val bank_10[] = { + {0x10bf, 0x69} +}; + +/* Clock dividers - these are default register values, divider = register + 1 */ +const static struct rj54n1_clock_div clk_div = { + .ratio_tg = 3 /* default: 5 */, + .ratio_t = 4 /* default: 1 */, + .ratio_r = 4 /* default: 0 */, + .ratio_op = 1 /* default: 5 */, + .ratio_o = 9 /* default: 0 */, +}; + +static struct rj54n1 *to_rj54n1(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct rj54n1, subdev); +} + +static int reg_read(struct i2c_client *client, const u16 reg) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + return i2c_smbus_read_byte_data(client, reg & 0xff); +} + +static int reg_write(struct i2c_client *client, const u16 reg, + const u8 data) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* set bank */ + if (rj54n1->bank != reg >> 8) { + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", 0xff, reg >> 8); + ret = i2c_smbus_write_byte_data(client, 0xff, reg >> 8); + if (ret < 0) + return ret; + rj54n1->bank = reg >> 8; + } + dev_dbg(&client->dev, "[0x%x] = 0x%x\n", reg & 0xff, data); + return i2c_smbus_write_byte_data(client, reg & 0xff, data); +} + +static int reg_set(struct i2c_client *client, const u16 reg, + const u8 data, const u8 mask) +{ + int ret; + + ret = reg_read(client, reg); + if (ret < 0) + return ret; + return reg_write(client, reg, (ret & ~mask) | (data & mask)); +} + +static int reg_write_multiple(struct i2c_client *client, + const struct rj54n1_reg_val *rv, const int n) +{ + int i, ret; + + for (i = 0; i < n; i++) { + ret = reg_write(client, rv->reg, rv->val); + if (ret < 0) + return ret; + rv++; + } + + return 0; +} + +static int rj54n1_s_stream(struct v4l2_subdev *sd, int enable) +{ + /* TODO: start / stop streaming */ + return 0; +} + +static int rj54n1_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + struct v4l2_subdev *sd = soc_camera_to_subdev(icd); + struct i2c_client *client = sd->priv; + /* Figures 2.5-1 to 2.5-3 - default falling pixclk edge */ + + if (flags & SOCAM_PCLK_SAMPLE_RISING) + return reg_write(client, RJ54N1_OUT_SIGPO, 1 << 4); + else + return reg_write(client, RJ54N1_OUT_SIGPO, 0); +} + +static unsigned long rj54n1_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + const unsigned long flags = + SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING | + SOCAM_MASTER | SOCAM_DATAWIDTH_8 | + SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int rj54n1_set_rect(struct i2c_client *client, + u16 reg_x, u16 reg_y, u16 reg_xy, + u32 width, u32 height) +{ + int ret; + + ret = reg_write(client, reg_xy, + ((width >> 4) & 0x70) | + ((height >> 8) & 7)); + + if (!ret) + ret = reg_write(client, reg_x, width & 0xff); + if (!ret) + ret = reg_write(client, reg_y, height & 0xff); + + return ret; +} + +/* + * Some commands, specifically certain initialisation sequences, require + * a commit operation. + */ +static int rj54n1_commit(struct i2c_client *client) +{ + int ret = reg_write(client, RJ54N1_INIT_START, 1); + msleep(10); + if (!ret) + ret = reg_write(client, RJ54N1_INIT_START, 0); + return ret; +} + +static int rj54n1_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + + a->c = rj54n1->rect; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int rj54n1_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = RJ54N1_COLUMN_SKIP; + a->bounds.top = RJ54N1_ROW_SKIP; + a->bounds.width = RJ54N1_MAX_WIDTH; + a->bounds.height = RJ54N1_MAX_HEIGHT; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int rj54n1_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->pixelformat = rj54n1->fourcc; + pix->field = V4L2_FIELD_NONE; + pix->width = rj54n1->width; + pix->height = rj54n1->height; + + return 0; +} + +/* + * The actual geometry configuration routine. It scales the input window into + * the output one, updates the window sizes and returns an error or the resize + * coefficient on success. Note: we only use the "Fixed Scaling" on this camera. + */ +static int rj54n1_sensor_scale(struct v4l2_subdev *sd, u32 *in_w, u32 *in_h, + u32 *out_w, u32 *out_h) +{ + struct i2c_client *client = sd->priv; + unsigned int skip, resize, input_w = *in_w, input_h = *in_h, + output_w = *out_w, output_h = *out_h; + u16 inc_sel; + int ret; + + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_S_L, + RJ54N1_Y_OUTPUT_SIZE_S_L, + RJ54N1_XY_OUTPUT_SIZE_S_H, output_w, output_h); + if (!ret) + ret = rj54n1_set_rect(client, RJ54N1_X_OUTPUT_SIZE_P_L, + RJ54N1_Y_OUTPUT_SIZE_P_L, + RJ54N1_XY_OUTPUT_SIZE_P_H, output_w, output_h); + + if (ret < 0) + return ret; + + if (output_w > input_w || output_h > input_h) { + input_w = output_w; + input_h = output_h; + + resize = 1024; + } else { + unsigned int resize_x, resize_y; + resize_x = input_w * 1024 / output_w; + resize_y = input_h * 1024 / output_h; + + resize = min(resize_x, resize_y); + + /* Prohibited value ranges */ + switch (resize) { + case 2040 ... 2047: + resize = 2039; + break; + case 4080 ... 4095: + resize = 4079; + break; + case 8160 ... 8191: + resize = 8159; + break; + case 16320 ... 16383: + resize = 16319; + } + + input_w = output_w * resize / 1024; + input_h = output_h * resize / 1024; + } + + /* Set scaling */ + ret = reg_write(client, RJ54N1_RESIZE_HOLD_L, resize & 0xff); + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_HOLD_H, resize >> 8); + + if (ret < 0) + return ret; + + /* + * Configure a skipping bitmask. The sensor will select a skipping value + * among set bits automatically. + */ + skip = min(resize / 1024, (unsigned)15); + inc_sel = 1 << skip; + + if (inc_sel <= 2) + inc_sel = 0xc; + else if (resize & 1023 && skip < 15) + inc_sel |= 1 << (skip + 1); + + ret = reg_write(client, RJ54N1_INC_USE_SEL_L, inc_sel & 0xfc); + if (!ret) + ret = reg_write(client, RJ54N1_INC_USE_SEL_H, inc_sel >> 8); + + /* Start resizing */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | RESIZE_GO | 1); + + if (ret < 0) + return ret; + + dev_dbg(&client->dev, "resize %u, skip %u\n", resize, skip); + + /* Constant taken from manufacturer's example */ + msleep(230); + + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, RESIZE_HOLD_SEL | 1); + if (ret < 0) + return ret; + + *in_w = input_w; + *in_h = input_h; + *out_w = output_w; + *out_h = output_h; + + return resize; +} + +static int rj54n1_set_clock(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + int ret; + + /* Enable external clock */ + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK | SOFT_STDBY); + /* Leave stand-by */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, E_EXCLK); + + if (!ret) + ret = reg_write(client, RJ54N1_PLL_L, 2); + if (!ret) + ret = reg_write(client, RJ54N1_PLL_N, 0x31); + + /* TGCLK dividers */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_TG, + rj54n1->clk_div.ratio_tg); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_T, + rj54n1->clk_div.ratio_t); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_R, + rj54n1->clk_div.ratio_r); + + /* Enable TGCLK & RAMP */ + if (!ret) + ret = reg_write(client, RJ54N1_RAMP_TGCLK_EN, 3); + + /* Disable clock output */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_DSP, 0); + + /* Set divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_OP, + rj54n1->clk_div.ratio_op); + if (!ret) + ret = reg_write(client, RJ54N1_RATIO_O, + rj54n1->clk_div.ratio_o); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + /* Use PLL for Timing Generator, write 2 to reserved bits */ + if (!ret) + ret = reg_write(client, RJ54N1_TG_BYPASS, 2); + + /* Take sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | SEN_RSTX); + /* Enable PLL */ + if (!ret) + ret = reg_write(client, RJ54N1_PLL_EN, 1); + + /* Wait for PLL to stabilise */ + msleep(10); + + /* Enable clock to frequency divider */ + if (!ret) + ret = reg_write(client, RJ54N1_CLK_RST, 1); + + if (!ret) + ret = reg_read(client, RJ54N1_CLK_RST); + if (ret != 1) { + dev_err(&client->dev, + "Resetting RJ54N1CB0C clock failed: %d!\n", ret); + return -EIO; + } + /* Start the PLL */ + ret = reg_set(client, RJ54N1_OCLK_DSP, 1, 1); + + /* Enable OCLK */ + if (!ret) + ret = reg_write(client, RJ54N1_OCLK_SEL_EN, 1); + + return ret; +} + +static int rj54n1_reg_init(struct i2c_client *client) +{ + int ret = rj54n1_set_clock(client); + + if (!ret) + ret = reg_write_multiple(client, bank_7, ARRAY_SIZE(bank_7)); + if (!ret) + ret = reg_write_multiple(client, bank_10, ARRAY_SIZE(bank_10)); + + /* Set binning divisors */ + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_1_2_LEV, 3 | (7 << 4)); + if (!ret) + ret = reg_write(client, RJ54N1_SCALE_4_LEV, 0xf); + + /* Switch to fixed resize mode */ + if (!ret) + ret = reg_write(client, RJ54N1_RESIZE_CONTROL, + RESIZE_HOLD_SEL | 1); + + /* Set gain */ + if (!ret) + ret = reg_write(client, RJ54N1_Y_GAIN, 0x84); + + /* Mirror the image back: default is upside down and left-to-right... */ + if (!ret) + ret = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 3, 3); + + if (!ret) + ret = reg_write_multiple(client, bank_4, ARRAY_SIZE(bank_4)); + if (!ret) + ret = reg_write_multiple(client, bank_5, ARRAY_SIZE(bank_5)); + if (!ret) + ret = reg_write_multiple(client, bank_8, ARRAY_SIZE(bank_8)); + + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | SEN_RSTX); + + /* Commit init */ + if (!ret) + ret = rj54n1_commit(client); + + /* Take DSP, TG, sensor out of reset */ + if (!ret) + ret = reg_write(client, RJ54N1_RESET_STANDBY, + E_EXCLK | DSP_RSTX | TG_RSTX | SEN_RSTX); + + if (!ret) + ret = reg_write(client, 0x7fe, 2); + + /* Constant taken from manufacturer's example */ + msleep(700); + + return ret; +} + +/* FIXME: streaming output only up to 800x600 is functional */ +static int rj54n1_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->field = V4L2_FIELD_NONE; + + if (pix->width > 800) + pix->width = 800; + if (pix->height > 600) + pix->height = 600; + + return 0; +} + +static int rj54n1_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f) +{ + struct i2c_client *client = sd->priv; + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct v4l2_pix_format *pix = &f->fmt.pix; + unsigned int output_w, output_h, + input_w = rj54n1->rect.width, input_h = rj54n1->rect.height; + int ret; + + /* + * The host driver can call us without .try_fmt(), so, we have to take + * care ourseleves + */ + ret = rj54n1_try_fmt(sd, f); + + /* + * Verify if the sensor has just been powered on. TODO: replace this + * with proper PM, when a suitable API is available. + */ + if (!ret) + ret = reg_read(client, RJ54N1_RESET_STANDBY); + if (ret < 0) + return ret; + + if (!(ret & E_EXCLK)) { + ret = rj54n1_reg_init(client); + if (ret < 0) + return ret; + } + + /* RA_SEL_UL is only relevant for raw modes, ignored otherwise. */ + switch (pix->pixelformat) { + case V4L2_PIX_FMT_YUYV: + ret = reg_write(client, RJ54N1_OUT_SEL, 0); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + case V4L2_PIX_FMT_RGB565: + ret = reg_write(client, RJ54N1_OUT_SEL, 0x11); + if (!ret) + ret = reg_set(client, RJ54N1_BYTE_SWAP, 8, 8); + break; + default: + ret = -EINVAL; + } + + if (ret < 0) + return ret; + + /* Supported scales 1:1 - 1:16 */ + if (pix->width < input_w / 16) + pix->width = input_w / 16; + if (pix->height < input_h / 16) + pix->height = input_h / 16; + + output_w = pix->width; + output_h = pix->height; + + ret = rj54n1_sensor_scale(sd, &input_w, &input_h, &output_w, &output_h); + if (ret < 0) + return ret; + + rj54n1->fourcc = pix->pixelformat; + rj54n1->resize = ret; + rj54n1->rect.width = input_w; + rj54n1->rect.height = input_h; + rj54n1->width = output_w; + rj54n1->height = output_h; + + pix->width = output_w; + pix->height = output_h; + pix->field = V4L2_FIELD_NONE; + + return ret; +} + +static int rj54n1_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = sd->priv; + + if (id->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; + + if (id->match.addr != client->addr) + return -ENODEV; + + id->ident = V4L2_IDENT_RJ54N1CB0C; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int rj54n1_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || + reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers > 0x0800 are only available from Sharp support */ + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + reg->size = 1; + reg->val = reg_read(client, reg->reg); + + if (reg->val > 0xff) + return -EIO; + + return 0; +} + +static int rj54n1_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = sd->priv; + + if (reg->match.type != V4L2_CHIP_MATCH_I2C_ADDR || + reg->reg < 0x400 || reg->reg > 0x1fff) + /* Registers >= 0x0800 are only available from Sharp support */ + return -EINVAL; + + if (reg->match.addr != client->addr) + return -ENODEV; + + if (reg_write(client, reg->reg, reg->val) < 0) + return -EIO; + + return 0; +} +#endif + +static const struct v4l2_queryctrl rj54n1_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_GAIN, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Gain", + .minimum = 0, + .maximum = 127, + .step = 1, + .default_value = 66, + .flags = V4L2_CTRL_FLAG_SLIDER, + }, +}; + +static struct soc_camera_ops rj54n1_ops = { + .set_bus_param = rj54n1_set_bus_param, + .query_bus_param = rj54n1_query_bus_param, + .controls = rj54n1_controls, + .num_controls = ARRAY_SIZE(rj54n1_controls), +}; + +static int rj54n1_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = sd->priv; + int data; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + data = reg_read(client, RJ54N1_MIRROR_STILL_MODE); + if (data < 0) + return -EIO; + ctrl->value = !(data & 1); + break; + case V4L2_CID_HFLIP: + data = reg_read(client, RJ54N1_MIRROR_STILL_MODE); + if (data < 0) + return -EIO; + ctrl->value = !(data & 2); + break; + case V4L2_CID_GAIN: + data = reg_read(client, RJ54N1_Y_GAIN); + if (data < 0) + return -EIO; + + ctrl->value = data / 2; + break; + } + + return 0; +} + +static int rj54n1_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + int data; + struct i2c_client *client = sd->priv; + const struct v4l2_queryctrl *qctrl; + + qctrl = soc_camera_find_qctrl(&rj54n1_ops, ctrl->id); + if (!qctrl) + return -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + if (ctrl->value) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 1); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 1, 1); + if (data < 0) + return -EIO; + break; + case V4L2_CID_HFLIP: + if (ctrl->value) + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 0, 2); + else + data = reg_set(client, RJ54N1_MIRROR_STILL_MODE, 2, 2); + if (data < 0) + return -EIO; + break; + case V4L2_CID_GAIN: + if (ctrl->value > qctrl->maximum || + ctrl->value < qctrl->minimum) + return -EINVAL; + else if (reg_write(client, RJ54N1_Y_GAIN, ctrl->value * 2) < 0) + return -EIO; + break; + } + + return 0; +} + +static struct v4l2_subdev_core_ops rj54n1_subdev_core_ops = { + .g_ctrl = rj54n1_g_ctrl, + .s_ctrl = rj54n1_s_ctrl, + .g_chip_ident = rj54n1_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = rj54n1_g_register, + .s_register = rj54n1_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops rj54n1_subdev_video_ops = { + .s_stream = rj54n1_s_stream, + .s_fmt = rj54n1_s_fmt, + .g_fmt = rj54n1_g_fmt, + .try_fmt = rj54n1_try_fmt, + .g_crop = rj54n1_g_crop, + .cropcap = rj54n1_cropcap, +}; + +static struct v4l2_subdev_ops rj54n1_subdev_ops = { + .core = &rj54n1_subdev_core_ops, + .video = &rj54n1_subdev_video_ops, +}; + +static int rj54n1_pin_config(struct i2c_client *client) +{ + /* + * Experimentally found out IOCTRL wired to 0. TODO: add to platform + * data: 0 or 1 << 7. + */ + return reg_write(client, RJ54N1_IOC, 0); +} + +/* + * Interface active, can use i2c. If it fails, it can indeed mean, that + * this wasn't our capture interface, so, we wait for the right one + */ +static int rj54n1_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + int data1, data2; + int ret; + + /* This could be a BUG_ON() or a WARN_ON(), or remove it completely */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) + return -ENODEV; + + /* Read out the chip version register */ + data1 = reg_read(client, RJ54N1_DEV_CODE); + data2 = reg_read(client, RJ54N1_DEV_CODE2); + + if (data1 != 0x51 || data2 != 0x10) { + ret = -ENODEV; + dev_info(&client->dev, "No RJ54N1CB0C found, read 0x%x:0x%x\n", + data1, data2); + goto ei2c; + } + + ret = rj54n1_pin_config(client); + if (ret < 0) + goto ei2c; + + dev_info(&client->dev, "Detected a RJ54N1CB0C chip ID 0x%x:0x%x\n", + data1, data2); + +ei2c: + return ret; +} + +static int rj54n1_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct rj54n1 *rj54n1; + struct soc_camera_device *icd = client->dev.platform_data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&client->dev, "RJ54N1CB0C: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&client->dev, "RJ54N1CB0C: missing platform data!\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_warn(&adapter->dev, + "I2C-Adapter doesn't support I2C_FUNC_SMBUS_BYTE\n"); + return -EIO; + } + + rj54n1 = kzalloc(sizeof(struct rj54n1), GFP_KERNEL); + if (!rj54n1) + return -ENOMEM; + + v4l2_i2c_subdev_init(&rj54n1->subdev, client, &rj54n1_subdev_ops); + + icd->ops = &rj54n1_ops; + + rj54n1->clk_div = clk_div; + rj54n1->rect.left = RJ54N1_COLUMN_SKIP; + rj54n1->rect.top = RJ54N1_ROW_SKIP; + rj54n1->rect.width = RJ54N1_MAX_WIDTH; + rj54n1->rect.height = RJ54N1_MAX_HEIGHT; + rj54n1->width = RJ54N1_MAX_WIDTH; + rj54n1->height = RJ54N1_MAX_HEIGHT; + rj54n1->fourcc = V4L2_PIX_FMT_YUYV; + rj54n1->resize = 1024; + + ret = rj54n1_video_probe(icd, client); + if (ret < 0) { + icd->ops = NULL; + i2c_set_clientdata(client, NULL); + kfree(rj54n1); + return ret; + } + + icd->formats = rj54n1_colour_formats; + icd->num_formats = ARRAY_SIZE(rj54n1_colour_formats); + + return ret; +} + +static int rj54n1_remove(struct i2c_client *client) +{ + struct rj54n1 *rj54n1 = to_rj54n1(client); + struct soc_camera_device *icd = client->dev.platform_data; + struct soc_camera_link *icl = to_soc_camera_link(icd); + + icd->ops = NULL; + if (icl->free_bus) + icl->free_bus(icl); + i2c_set_clientdata(client, NULL); + client->driver = NULL; + kfree(rj54n1); + + return 0; +} + +static const struct i2c_device_id rj54n1_id[] = { + { "rj54n1cb0c", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rj54n1_id); + +static struct i2c_driver rj54n1_i2c_driver = { + .driver = { + .name = "rj54n1cb0c", + }, + .probe = rj54n1_probe, + .remove = rj54n1_remove, + .id_table = rj54n1_id, +}; + +static int __init rj54n1_mod_init(void) +{ + return i2c_add_driver(&rj54n1_i2c_driver); +} + +static void __exit rj54n1_mod_exit(void) +{ + i2c_del_driver(&rj54n1_i2c_driver); +} + +module_init(rj54n1_mod_init); +module_exit(rj54n1_mod_exit); + +MODULE_DESCRIPTION("Sharp RJ54N1CB0C Camera driver"); +MODULE_AUTHOR("Guennadi Liakhovetski "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 03d39266d293c..41765f3c7c28c 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -1958,7 +1958,7 @@ static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) if (pdword[1] >= MAX_CHANNELS) break; cc = G_chnmap[pdword[1]]; - if (!(cc >= 0 && cc < MAX_CHANNELS)) + if (cc >= MAX_CHANNELS) break; switch (pdword[2]) { case S2255_RESPONSE_SETMODE: diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c index 5c24c993ac167..3bca744e43af1 100644 --- a/drivers/media/video/saa7110.c +++ b/drivers/media/video/saa7110.c @@ -304,7 +304,7 @@ static int saa7110_s_routing(struct v4l2_subdev *sd, { struct saa7110 *decoder = to_saa7110(sd); - if (input < 0 || input >= SAA7110_MAX_INPUT) { + if (input >= SAA7110_MAX_INPUT) { v4l2_dbg(1, debug, sd, "input=%d not available\n", input); return -EINVAL; } diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 09013229d4aa3..7e40d6d99dd08 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5239,6 +5239,7 @@ struct saa7134_board saa7134_boards[] = { .radio_type = UNSET, .tuner_addr = ADDR_UNSET, .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, .inputs = { { .name = name_tv, .vmux = 2, @@ -5279,6 +5280,46 @@ struct saa7134_board saa7134_boards[] = { .amux = TV, }, }, + [SAA7134_BOARD_ASUS_EUROPA_HYBRID] = { + .name = "Asus Europa Hybrid OEM", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TD1316, + .radio_type = UNSET, + .tuner_addr = 0x61, + .radio_addr = ADDR_UNSET, + .tda9887_conf = TDA9887_PRESENT | TDA9887_PORT1_ACTIVE, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_tv, + .vmux = 3, + .amux = TV, + .tv = 1, + }, { + .name = name_comp1, + .vmux = 4, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + }, + [SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S] = { + .name = "Leadtek Winfast DTV1000S", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .mpeg = SAA7134_MPEG_DVB, + .inputs = { { + .name = name_comp1, + .vmux = 3, + }, { + .name = name_svideo, + .vmux = 8, + } }, + }, }; @@ -6417,6 +6458,18 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = PCI_VENDOR_ID_PHILIPS, .subdevice = 0x2004, .driver_data = SAA7134_BOARD_ZOLID_HYBRID_PCI, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7134, + .subvendor = 0x1043, + .subdevice = 0x4847, + .driver_data = SAA7134_BOARD_ASUS_EUROPA_HYBRID, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7130, + .subvendor = 0x107d, + .subdevice = 0x6655, + .driver_data = SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S, }, { /* --- boards without eeprom + subsystem ID --- */ .vendor = PCI_VENDOR_ID_PHILIPS, @@ -6748,6 +6801,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG: case SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS: case SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM: + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: dev->has_remote = SAA7134_REMOTE_GPIO; break; case SAA7134_BOARD_FLYDVBS_LR300: @@ -7079,6 +7133,7 @@ int saa7134_board_init2(struct saa7134_dev *dev) /* break intentionally omitted */ case SAA7134_BOARD_VIDEOMATE_DVBT_300: case SAA7134_BOARD_ASUS_EUROPA2_HYBRID: + case SAA7134_BOARD_ASUS_EUROPA_HYBRID: { /* The Philips EUROPA based hybrid boards have the tuner diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c index c673901cb2b51..0ba7f5af0fc33 100644 --- a/drivers/media/video/saa7134/saa7134-core.c +++ b/drivers/media/video/saa7134/saa7134-core.c @@ -1032,7 +1032,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev, saa7134_irq_video_signalchange(dev); if (TUNER_ABSENT != dev->tuner_type) - saa_call_all(dev, tuner, s_standby); + saa_call_all(dev, core, s_power, 0); /* register v4l devices */ if (saa7134_no_overlay > 0) diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index a26e997a9ce6c..73739d2a63dd5 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -40,6 +40,7 @@ #include "tda1004x.h" #include "nxt200x.h" #include "tuner-xc2028.h" +#include "xc5000.h" #include "tda10086.h" #include "tda826x.h" @@ -871,6 +872,20 @@ static struct zl10353_config behold_h6_config = { .disable_i2c_gate_ctrl = 1, }; +static struct xc5000_config behold_x7_tunerconfig = { + .i2c_address = 0xc2>>1, + .if_khz = 4560, + .radio_input = XC5000_RADIO_FM1, +}; + +static struct zl10353_config behold_x7_config = { + .demod_address = 0x1e>>1, + .if2 = 45600, + .no_tuner = 1, + .parallel_ts = 1, + .disable_i2c_gate_ctrl = 1, +}; + /* ================================================================== * tda10086 based DVB-S cards, helper functions */ @@ -1030,6 +1045,32 @@ static struct tda18271_config zolid_tda18271_config = { .gate = TDA18271_GATE_ANALOG, }; +static struct tda10048_config dtv1000s_tda10048_config = { + .demod_address = 0x10 >> 1, + .output_mode = TDA10048_PARALLEL_OUTPUT, + .fwbulkwritelen = TDA10048_BULKWRITE_200, + .inversion = TDA10048_INVERSION_ON, + .dtv6_if_freq_khz = TDA10048_IF_3300, + .dtv7_if_freq_khz = TDA10048_IF_3800, + .dtv8_if_freq_khz = TDA10048_IF_4300, + .clk_freq_khz = TDA10048_CLK_16000, + .disable_gate_access = 1, +}; + +static struct tda18271_std_map dtv1000s_tda18271_std_map = { + .dvbt_6 = { .if_freq = 3300, .agc_mode = 3, .std = 4, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_7 = { .if_freq = 3800, .agc_mode = 3, .std = 5, + .if_lvl = 1, .rfagc_top = 0x37, }, + .dvbt_8 = { .if_freq = 4300, .agc_mode = 3, .std = 6, + .if_lvl = 1, .rfagc_top = 0x37, }, +}; + +static struct tda18271_config dtv1000s_tda18271_config = { + .std_map = &dtv1000s_tda18271_std_map, + .gate = TDA18271_GATE_ANALOG, +}; + /* ================================================================== * Core code */ @@ -1116,6 +1157,7 @@ static int dvb_init(struct saa7134_dev *dev) break; case SAA7134_BOARD_PHILIPS_EUROPA: case SAA7134_BOARD_VIDEOMATE_DVBT_300: + case SAA7134_BOARD_ASUS_EUROPA_HYBRID: fe0->dvb.frontend = dvb_attach(tda10046_attach, &philips_europa_config, &dev->i2c_adap); @@ -1482,6 +1524,15 @@ static int dvb_init(struct saa7134_dev *dev) TUNER_PHILIPS_FMD1216MEX_MK3); } break; + case SAA7134_BOARD_BEHOLD_X7: + fe0->dvb.frontend = dvb_attach(zl10353_attach, + &behold_x7_config, + &dev->i2c_adap); + if (fe0->dvb.frontend) { + dvb_attach(xc5000_attach, fe0->dvb.frontend, + &dev->i2c_adap, &behold_x7_tunerconfig); + } + break; case SAA7134_BOARD_AVERMEDIA_A700_PRO: case SAA7134_BOARD_AVERMEDIA_A700_HYBRID: /* Zarlink ZL10313 */ @@ -1518,6 +1569,19 @@ static int dvb_init(struct saa7134_dev *dev) &zolid_tda18271_config); } break; + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + fe0->dvb.frontend = dvb_attach(tda10048_attach, + &dtv1000s_tda10048_config, + &dev->i2c_adap); + if (fe0->dvb.frontend != NULL) { + dvb_attach(tda829x_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0x4b, + &tda829x_no_probe); + dvb_attach(tda18271_attach, fe0->dvb.frontend, + 0x60, &dev->i2c_adap, + &dtv1000s_tda18271_config); + } + break; default: wprintk("Huh? unknown DVB card?\n"); break; @@ -1550,7 +1614,7 @@ static int dvb_init(struct saa7134_dev *dev) /* register everything else */ ret = videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev, - &dev->pci->dev, adapter_nr, 0); + &dev->pci->dev, adapter_nr, 0, NULL); /* this sequence is necessary to make the tda1004x load its firmware * and to enter analog mode of hybrid boards diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index a0e8c62e6ae16..744918b1cd472 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -102,14 +102,14 @@ static int build_key(struct saa7134_dev *dev) if (data == ir->mask_keycode) ir_input_nokey(ir->dev, &ir->ir); else - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); return 0; } if (ir->polling) { if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); } else { ir_input_nokey(ir->dev, &ir->ir); } @@ -117,7 +117,7 @@ static int build_key(struct saa7134_dev *dev) else { /* IRQ driven mode - handle key press and release in one go */ if ((ir->mask_keydown && (0 != (gpio & ir->mask_keydown))) || (ir->mask_keyup && (0 == (gpio & ir->mask_keyup)))) { - ir_input_keydown(ir->dev, &ir->ir, data, data); + ir_input_keydown(ir->dev, &ir->ir, data); ir_input_nokey(ir->dev, &ir->ir); } } @@ -616,6 +616,12 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0x003f00; mask_keydown = 0x040000; break; + case SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S: + ir_codes = &ir_codes_winfast_table; + mask_keycode = 0x5f00; + mask_keyup = 0x020000; + polling = 50; /* ms */ + break; } if (NULL == ir_codes) { printk("%s: Oops: IR config error [card=%d]\n", @@ -646,7 +652,10 @@ int saa7134_input_init1(struct saa7134_dev *dev) snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(dev->pci)); - ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + err = ir_input_init(input_dev, &ir->ir, ir_type, ir_codes); + if (err < 0) + goto err_out_free; + input_dev->name = ir->name; input_dev->phys = ir->phys; input_dev->id.bustype = BUS_PCI; @@ -677,6 +686,7 @@ int saa7134_input_init1(struct saa7134_dev *dev) saa7134_ir_stop(dev); dev->remote = NULL; err_out_free: + ir_input_free(input_dev); input_free_device(input_dev); kfree(ir); return err; @@ -688,6 +698,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) return; saa7134_ir_stop(dev); + ir_input_free(dev->remote->dev); input_unregister_device(dev->remote->dev); kfree(dev->remote); dev->remote = NULL; @@ -695,10 +706,7 @@ void saa7134_input_fini(struct saa7134_dev *dev) void saa7134_probe_i2c_ir(struct saa7134_dev *dev) { - const unsigned short addr_list[] = { - 0x7a, 0x47, 0x71, 0x2d, - I2C_CLIENT_END - }; + struct i2c_board_info info; struct i2c_msg msg_msi = { .addr = 0x50, @@ -714,9 +722,9 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) return; } - memset(&dev->info, 0, sizeof(dev->info)); + memset(&info, 0, sizeof(struct i2c_board_info)); memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(dev->info.type, "ir_video", I2C_NAME_SIZE); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); switch (dev->board) { case SAA7134_BOARD_PINNACLE_PCTV_110i: @@ -725,23 +733,24 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) if (pinnacle_remote == 0) { dev->init_data.get_key = get_key_pinnacle_color; dev->init_data.ir_codes = &ir_codes_pinnacle_color_table; - dev->info.addr = 0x47; + info.addr = 0x47; } else { dev->init_data.get_key = get_key_pinnacle_grey; dev->init_data.ir_codes = &ir_codes_pinnacle_grey_table; - dev->info.addr = 0x47; + info.addr = 0x47; } break; case SAA7134_BOARD_UPMOST_PURPLE_TV: dev->init_data.name = "Purple TV"; dev->init_data.get_key = get_key_purpletv; dev->init_data.ir_codes = &ir_codes_purpletv_table; + info.addr = 0x7a; break; case SAA7134_BOARD_MSI_TVATANYWHERE_PLUS: dev->init_data.name = "MSI TV@nywhere Plus"; dev->init_data.get_key = get_key_msi_tvanywhere_plus; dev->init_data.ir_codes = &ir_codes_msi_tvanywhere_plus_table; - dev->info.addr = 0x30; + info.addr = 0x30; /* MSI TV@nywhere Plus controller doesn't seem to respond to probes unless we read something from an existing device. Weird... @@ -755,6 +764,7 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "HVR 1110"; dev->init_data.get_key = get_key_hvr1110; dev->init_data.ir_codes = &ir_codes_hauppauge_new_table; + info.addr = 0x71; break; case SAA7134_BOARD_BEHOLD_607FM_MK3: case SAA7134_BOARD_BEHOLD_607FM_MK5: @@ -772,23 +782,20 @@ void saa7134_probe_i2c_ir(struct saa7134_dev *dev) dev->init_data.name = "BeholdTV"; dev->init_data.get_key = get_key_beholdm6xx; dev->init_data.ir_codes = &ir_codes_behold_table; + info.addr = 0x2d; break; case SAA7134_BOARD_AVERMEDIA_CARDBUS_501: case SAA7134_BOARD_AVERMEDIA_CARDBUS_506: - dev->info.addr = 0x40; + info.addr = 0x40; break; - } - - if (dev->init_data.name) - dev->info.platform_data = &dev->init_data; - /* No need to probe if address is known */ - if (dev->info.addr) { - i2c_new_device(&dev->i2c_adap, &dev->info); + default: + dprintk("No I2C IR support for board %x\n", dev->board); return; } - /* Address not known, fallback to probing */ - i2c_new_probed_device(&dev->i2c_adap, &dev->info, addr_list); + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_device(&dev->i2c_adap, &info); } static int saa7134_rc5_irq(struct saa7134_dev *dev) @@ -936,7 +943,7 @@ static void nec_task(unsigned long data) dprintk("scancode = 0x%02x (code = 0x%02x, notcode= 0x%02x)\n", ir->code, ircode, not_code); - ir_input_keydown(ir->dev, &ir->ir, ir->code, ir->code); + ir_input_keydown(ir->dev, &ir->ir, ir->code); } else dprintk("Repeat last key\n"); diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index da26f476a302f..35f8daa3a359c 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -1499,7 +1499,7 @@ static int video_release(struct file *file) saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0); saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0); - saa_call_all(dev, tuner, s_standby); + saa_call_all(dev, core, s_power, 0); if (fh->radio) saa_call_all(dev, core, ioctl, RDS_CMD_CLOSE, &cmd); diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index f8697d46ff5fe..53b7e0b8a2fbd 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -297,6 +297,8 @@ struct saa7134_format { #define SAA7134_BOARD_BEHOLD_X7 171 #define SAA7134_BOARD_ROVERMEDIA_LINK_PRO_FM 172 #define SAA7134_BOARD_ZOLID_HYBRID_PCI 173 +#define SAA7134_BOARD_ASUS_EUROPA_HYBRID 174 +#define SAA7134_BOARD_LEADTEK_WINFAST_DTV1000S 175 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 @@ -592,7 +594,6 @@ struct saa7134_dev { unsigned int insuspend; /* I2C keyboard data */ - struct i2c_board_info info; struct IR_i2c_init_data init_data; /* SAA7134_MPEG_* */ diff --git a/drivers/media/video/saa7164/saa7164-dvb.c b/drivers/media/video/saa7164/saa7164-dvb.c index 6a2d847d6a881..cf099c59b38e2 100644 --- a/drivers/media/video/saa7164/saa7164-dvb.c +++ b/drivers/media/video/saa7164/saa7164-dvb.c @@ -68,6 +68,7 @@ static struct tda18271_config hauppauge_hvr22x0s_tuner_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, .role = TDA18271_SLAVE, + .output_opt = TDA18271_OUTPUT_LT_OFF, .rf_cal_on_startup = 1 }; diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c index b15c40908e842..6818df5711686 100644 --- a/drivers/media/video/saa717x.c +++ b/drivers/media/video/saa717x.c @@ -1115,7 +1115,7 @@ static int saa717x_s_video_routing(struct v4l2_subdev *sd, v4l2_dbg(1, debug, sd, "decoder set input (%d)\n", input); /* inputs from 0-9 are available*/ /* saa717x have mode0-mode9 but mode5 is reserved. */ - if (input < 0 || input > 9 || input == 5) + if (input > 9 || input == 5) return -EINVAL; if (decoder->input != input) { @@ -1312,7 +1312,7 @@ static int saa717x_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt) "MONO", "STEREO", "LANG1", "LANG2/SAP" }; - audio_mode = V4L2_TUNER_MODE_STEREO; + audio_mode = TUNER_AUDIO_STEREO; switch (vt->audmode) { case V4L2_TUNER_MODE_MONO: diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 9c8b7c7b89ee6..a4f3472d4db87 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -153,6 +153,40 @@ static u32 ceu_read(struct sh_mobile_ceu_dev *priv, unsigned long reg_offs) return ioread32(priv->base + reg_offs); } +static int sh_mobile_ceu_soft_reset(struct sh_mobile_ceu_dev *pcdev) +{ + int i, success = 0; + struct soc_camera_device *icd = pcdev->icd; + + ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ + + /* wait CSTSR.CPTON bit */ + for (i = 0; i < 1000; i++) { + if (!(ceu_read(pcdev, CSTSR) & 1)) { + success++; + break; + } + udelay(1); + } + + /* wait CAPSR.CPKIL bit */ + for (i = 0; i < 1000; i++) { + if (!(ceu_read(pcdev, CAPSR) & (1 << 16))) { + success++; + break; + } + udelay(1); + } + + + if (2 != success) { + dev_warn(&icd->dev, "soft reset time out\n"); + return -EIO; + } + + return 0; +} + /* * Videobuf operations */ @@ -202,26 +236,45 @@ static void free_buffer(struct videobuf_queue *vq, #define CEU_CETCR_MAGIC 0x0317f313 /* acknowledge magical interrupt sources */ #define CEU_CETCR_IGRW (1 << 4) /* prohibited register access interrupt bit */ #define CEU_CEIER_CPEIE (1 << 0) /* one-frame capture end interrupt */ +#define CEU_CEIER_VBP (1 << 20) /* vbp error */ #define CEU_CAPCR_CTNCP (1 << 16) /* continuous capture mode (if set) */ +#define CEU_CEIER_MASK (CEU_CEIER_CPEIE | CEU_CEIER_VBP) -static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) +/* + * return value doesn't reflex the success/failure to queue the new buffer, + * but rather the status of the previous buffer. + */ +static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) { struct soc_camera_device *icd = pcdev->icd; dma_addr_t phys_addr_top, phys_addr_bottom; + u32 status; + int ret = 0; /* The hardware is _very_ picky about this sequence. Especially * the CEU_CETCR_MAGIC value. It seems like we need to acknowledge * several not-so-well documented interrupt sources in CETCR. */ - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_CPEIE); - ceu_write(pcdev, CETCR, ~ceu_read(pcdev, CETCR) & CEU_CETCR_MAGIC); - ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_CPEIE); + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) & ~CEU_CEIER_MASK); + status = ceu_read(pcdev, CETCR); + ceu_write(pcdev, CETCR, ~status & CEU_CETCR_MAGIC); + ceu_write(pcdev, CEIER, ceu_read(pcdev, CEIER) | CEU_CEIER_MASK); ceu_write(pcdev, CAPCR, ceu_read(pcdev, CAPCR) & ~CEU_CAPCR_CTNCP); ceu_write(pcdev, CETCR, CEU_CETCR_MAGIC ^ CEU_CETCR_IGRW); + /* + * When a VBP interrupt occurs, a capture end interrupt does not occur + * and the image of that frame is not captured correctly. So, soft reset + * is needed here. + */ + if (status & CEU_CEIER_VBP) { + sh_mobile_ceu_soft_reset(pcdev); + ret = -EIO; + } + if (!pcdev->active) - return; + return ret; phys_addr_top = videobuf_to_dma_contig(pcdev->active); ceu_write(pcdev, CDAYR, phys_addr_top); @@ -247,6 +300,8 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) pcdev->active->state = VIDEOBUF_ACTIVE; ceu_write(pcdev, CAPSR, 0x1); /* start capture */ + + return ret; } static int sh_mobile_ceu_videobuf_prepare(struct videobuf_queue *vq, @@ -319,6 +374,11 @@ static void sh_mobile_ceu_videobuf_queue(struct videobuf_queue *vq, list_add_tail(&vb->queue, &pcdev->capture); if (!pcdev->active) { + /* + * Because there were no active buffer at this moment, + * we are not interested in the return value of + * sh_mobile_ceu_capture here. + */ pcdev->active = vb; sh_mobile_ceu_capture(pcdev); } @@ -379,9 +439,8 @@ static irqreturn_t sh_mobile_ceu_irq(int irq, void *data) else pcdev->active = NULL; - sh_mobile_ceu_capture(pcdev); - - vb->state = VIDEOBUF_DONE; + vb->state = (sh_mobile_ceu_capture(pcdev) < 0) ? + VIDEOBUF_ERROR : VIDEOBUF_DONE; do_gettimeofday(&vb->ts); vb->field_count++; wake_up(&vb->done); @@ -407,13 +466,9 @@ static int sh_mobile_ceu_add_device(struct soc_camera_device *icd) pm_runtime_get_sync(ici->v4l2_dev.dev); - ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ - while (ceu_read(pcdev, CSTSR) & 1) - msleep(1); - pcdev->icd = icd; - return 0; + return sh_mobile_ceu_soft_reset(pcdev); } /* Called with .video_lock held */ @@ -427,7 +482,7 @@ static void sh_mobile_ceu_remove_device(struct soc_camera_device *icd) /* disable capture, disable interrupts */ ceu_write(pcdev, CEIER, 0); - ceu_write(pcdev, CAPSR, 1 << 16); /* reset */ + sh_mobile_ceu_soft_reset(pcdev); /* make sure active buffer is canceled */ spin_lock_irqsave(&pcdev->lock, flags); diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index aba92e2313d8a..5b3eaa16afd22 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -320,6 +320,7 @@ static void set_type(struct i2c_client *c, unsigned int type, struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops; struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; unsigned char buffer[4]; + int tune_now = 1; if (type == UNSET || type == TUNER_ABSENT) { tuner_dbg ("tuner 0x%02x: Tuner type absent\n",c->addr); @@ -328,7 +329,7 @@ static void set_type(struct i2c_client *c, unsigned int type, t->type = type; /* prevent invalid config values */ - t->config = ((new_config >= 0) && (new_config < 256)) ? new_config : 0; + t->config = new_config < 256 ? new_config : 0; if (tuner_callback != NULL) { tuner_dbg("defining GPIO callback\n"); t->fe.callback = tuner_callback; @@ -404,6 +405,7 @@ static void set_type(struct i2c_client *c, unsigned int type, }; if (!dvb_attach(xc2028_attach, &t->fe, &cfg)) goto attach_failed; + tune_now = 0; break; } case TUNER_TDA9887: @@ -419,6 +421,7 @@ static void set_type(struct i2c_client *c, unsigned int type, if (!dvb_attach(xc5000_attach, &t->fe, t->i2c->adapter, &xc5000_cfg)) goto attach_failed; + tune_now = 0; break; } case TUNER_NXP_TDA18271: @@ -430,6 +433,7 @@ static void set_type(struct i2c_client *c, unsigned int type, if (!dvb_attach(tda18271_attach, &t->fe, t->i2c->addr, t->i2c->adapter, &cfg)) goto attach_failed; + tune_now = 0; break; } default: @@ -458,12 +462,13 @@ static void set_type(struct i2c_client *c, unsigned int type, if (t->mode_mask == T_UNINITIALIZED) t->mode_mask = new_mode_mask; - /* xc2028/3028 and xc5000 requires a firmware to be set-up later + /* Some tuners require more initialization setup before use, + such as firmware download or device calibration. trying to set a frequency here will just fail FIXME: better to move set_freq to the tuner code. This is needed on analog tuners for PLL to properly work */ - if (t->type != TUNER_XC2028 && t->type != TUNER_XC5000) + if (tune_now) set_freq(c, (V4L2_TUNER_RADIO == t->mode) ? t->radio_freq : t->tv_freq); @@ -752,14 +757,17 @@ static int tuner_s_radio(struct v4l2_subdev *sd) return 0; } -static int tuner_s_standby(struct v4l2_subdev *sd) +static int tuner_s_power(struct v4l2_subdev *sd, int on) { struct tuner *t = to_tuner(sd); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; + if (on) + return 0; + tuner_dbg("Putting tuner to sleep\n"); - if (check_mode(t, "s_standby") == -EINVAL) + if (check_mode(t, "s_power") == -EINVAL) return 0; t->mode = T_STANDBY; if (analog_ops->standby) @@ -961,6 +969,7 @@ static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) static const struct v4l2_subdev_core_ops tuner_core_ops = { .log_status = tuner_log_status, .s_std = tuner_s_std, + .s_power = tuner_s_power, }; static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { @@ -971,7 +980,6 @@ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = { .g_frequency = tuner_g_frequency, .s_type_addr = tuner_s_type_addr, .s_config = tuner_s_config, - .s_standby = tuner_s_standby, }; static const struct v4l2_subdev_ops tuner_ops = { diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index 0869bafc2b56a..800fc1b111eff 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1919,7 +1919,7 @@ static const struct v4l2_subdev_tuner_ops tvaudio_tuner_ops = { .s_radio = tvaudio_s_radio, .s_frequency = tvaudio_s_frequency, .s_tuner = tvaudio_s_tuner, - .s_tuner = tvaudio_g_tuner, + .g_tuner = tvaudio_g_tuner, }; static const struct v4l2_subdev_audio_ops tvaudio_audio_ops = { diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c index 244372627df29..26b4e718cd6d7 100644 --- a/drivers/media/video/tvp514x.c +++ b/drivers/media/video/tvp514x.c @@ -272,7 +272,7 @@ static int tvp514x_read_reg(struct v4l2_subdev *sd, u8 reg) read_again: err = i2c_smbus_read_byte_data(client, reg); - if (err == -1) { + if (err < 0) { if (retry <= I2C_RETRY_COUNT) { v4l2_warn(sd, "Read: retry ... %d\n", retry); retry++; diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c index 31d57f2d09e1b..a0addcb042950 100644 --- a/drivers/media/video/usbvideo/konicawc.c +++ b/drivers/media/video/usbvideo/konicawc.c @@ -225,7 +225,7 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); - strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname)); cam->input = input_dev = input_allocate_device(); if (!input_dev) { diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c index 803d3e4e29a20..c4d1b96b5cee3 100644 --- a/drivers/media/video/usbvideo/quickcam_messenger.c +++ b/drivers/media/video/usbvideo/quickcam_messenger.c @@ -89,7 +89,7 @@ static void qcm_register_input(struct qcm *cam, struct usb_device *dev) int error; usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname)); - strncat(cam->input_physname, "/input0", sizeof(cam->input_physname)); + strlcat(cam->input_physname, "/input0", sizeof(cam->input_physname)); cam->input = input_dev = input_allocate_device(); if (!input_dev) { diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index a2a50d608a3fd..c07b0ac452abf 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -601,7 +601,7 @@ static int vidioc_s_input (struct file *file, void *priv, unsigned int input) { struct usb_usbvision *usbvision = video_drvdata(file); - if ((input >= usbvision->video_inputs) || (input < 0) ) + if (input >= usbvision->video_inputs) return -EINVAL; mutex_lock(&usbvision->lock); diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 1b89735e62fdb..0469d7a876a89 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -742,17 +742,7 @@ struct uvc_control *uvc_find_control(struct uvc_video_chain *chain, v4l2_id &= V4L2_CTRL_ID_MASK; /* Find the control. */ - __uvc_find_control(chain->processing, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - - list_for_each_entry(entity, &chain->iterms, chain) { - __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); - if (ctrl && !next) - return ctrl; - } - - list_for_each_entry(entity, &chain->extensions, chain) { + list_for_each_entry(entity, &chain->entities, chain) { __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); if (ctrl && !next) return ctrl; @@ -826,6 +816,13 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, ret = 0; goto out; + case V4L2_CTRL_TYPE_BUTTON: + v4l2_ctrl->minimum = 0; + v4l2_ctrl->maximum = 0; + v4l2_ctrl->step = 0; + ret = 0; + goto out; + default: break; } @@ -944,17 +941,7 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) int ret = 0; /* Find the control. */ - ret = uvc_ctrl_commit_entity(chain->dev, chain->processing, rollback); - if (ret < 0) - goto done; - - list_for_each_entry(entity, &chain->iterms, chain) { - ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); - if (ret < 0) - goto done; - } - - list_for_each_entry(entity, &chain->extensions, chain) { + list_for_each_entry(entity, &chain->entities, chain) { ret = uvc_ctrl_commit_entity(chain->dev, entity, rollback); if (ret < 0) goto done; @@ -1068,8 +1055,9 @@ int uvc_xu_ctrl_query(struct uvc_video_chain *chain, int ret; /* Find the extension unit. */ - list_for_each_entry(entity, &chain->extensions, chain) { - if (entity->id == xctrl->unit) + list_for_each_entry(entity, &chain->entities, chain) { + if (UVC_ENTITY_TYPE(entity) == UVC_VC_EXTENSION_UNIT && + entity->id == xctrl->unit) break; } diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c index 8756be5691544..c31bc50113bca 100644 --- a/drivers/media/video/uvc/uvc_driver.c +++ b/drivers/media/video/uvc/uvc_driver.c @@ -46,6 +46,7 @@ unsigned int uvc_no_drop_param; static unsigned int uvc_quirks_param; unsigned int uvc_trace_param; +unsigned int uvc_timeout_param = UVC_CTRL_STREAMING_TIMEOUT; /* ------------------------------------------------------------------------ * Video formats @@ -248,29 +249,9 @@ static struct uvc_entity *uvc_entity_by_reference(struct uvc_device *dev, entity = list_entry(&dev->entities, struct uvc_entity, list); list_for_each_entry_continue(entity, &dev->entities, list) { - switch (UVC_ENTITY_TYPE(entity)) { - case UVC_TT_STREAMING: - if (entity->output.bSourceID == id) - return entity; - break; - - case UVC_VC_PROCESSING_UNIT: - if (entity->processing.bSourceID == id) + for (i = 0; i < entity->bNrInPins; ++i) + if (entity->baSourceID[i] == id) return entity; - break; - - case UVC_VC_SELECTOR_UNIT: - for (i = 0; i < entity->selector.bNrInPins; ++i) - if (entity->selector.baSourceID[i] == id) - return entity; - break; - - case UVC_VC_EXTENSION_UNIT: - for (i = 0; i < entity->extension.bNrInPins; ++i) - if (entity->extension.baSourceID[i] == id) - return entity; - break; - } } return NULL; @@ -426,7 +407,8 @@ static int uvc_parse_format(struct uvc_device *dev, /* Parse the frame descriptors. Only uncompressed, MJPEG and frame * based formats have frame descriptors. */ - while (buflen > 2 && buffer[2] == ftype) { + while (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == ftype) { frame = &format->frame[format->nframes]; if (ftype != UVC_VS_FRAME_FRAME_BASED) n = buflen > 25 ? buffer[25] : 0; @@ -503,12 +485,14 @@ static int uvc_parse_format(struct uvc_device *dev, buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_STILL_IMAGE_FRAME) { buflen -= buffer[0]; buffer += buffer[0]; } - if (buflen > 2 && buffer[2] == UVC_VS_COLORFORMAT) { + if (buflen > 2 && buffer[1] == USB_DT_CS_INTERFACE && + buffer[2] == UVC_VS_COLORFORMAT) { if (buflen < 6) { uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming " "interface %d COLORFORMAT error\n", @@ -749,6 +733,11 @@ static int uvc_parse_streaming(struct uvc_device *dev, buffer += buffer[0]; } + if (buflen) + uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming interface " + "%d has %u bytes of trailing descriptor garbage.\n", + dev->udev->devnum, alts->desc.bInterfaceNumber, buflen); + /* Parse the alternate settings to find the maximum bandwidth. */ for (i = 0; i < intf->num_altsetting; ++i) { struct usb_host_endpoint *ep; @@ -776,6 +765,28 @@ static int uvc_parse_streaming(struct uvc_device *dev, return ret; } +static struct uvc_entity *uvc_alloc_entity(u16 type, u8 id, + unsigned int num_pads, unsigned int extra_size) +{ + struct uvc_entity *entity; + unsigned int num_inputs; + unsigned int size; + + num_inputs = (type & UVC_TERM_OUTPUT) ? num_pads : num_pads - 1; + size = sizeof(*entity) + extra_size + num_inputs; + entity = kzalloc(size, GFP_KERNEL); + if (entity == NULL) + return NULL; + + entity->id = id; + entity->type = type; + + entity->bNrInPins = num_inputs; + entity->baSourceID = ((__u8 *)entity) + sizeof(*entity) + extra_size; + + return entity; +} + /* Parse vendor-specific extensions. */ static int uvc_parse_vendor_control(struct uvc_device *dev, const unsigned char *buffer, int buflen) @@ -827,21 +838,18 @@ static int uvc_parse_vendor_control(struct uvc_device *dev, break; } - unit = kzalloc(sizeof *unit + p + 2*n, GFP_KERNEL); + unit = uvc_alloc_entity(UVC_VC_EXTENSION_UNIT, buffer[3], + p + 1, 2*n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = UVC_VC_EXTENSION_UNIT; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); - unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.baSourceID, &buffer[22], p); + memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; - unit->extension.bmControlsType = (__u8 *)unit + sizeof *unit - + p + n; + unit->extension.bmControls = (__u8 *)unit + sizeof(*unit); + unit->extension.bmControlsType = (__u8 *)unit + sizeof(*unit) + + n; memcpy(unit->extension.bmControls, &buffer[23+p], 2*n); if (buffer[24+p+2*n] != 0) @@ -938,13 +946,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - term = kzalloc(sizeof *term + n + p, GFP_KERNEL); + term = uvc_alloc_entity(type | UVC_TERM_INPUT, buffer[3], + 1, n + p); if (term == NULL) return -ENOMEM; - term->id = buffer[3]; - term->type = type | UVC_TERM_INPUT; - if (UVC_ENTITY_TYPE(term) == UVC_ITT_CAMERA) { term->camera.bControlSize = n; term->camera.bmControls = (__u8 *)term + sizeof *term; @@ -999,13 +1005,12 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return 0; } - term = kzalloc(sizeof *term, GFP_KERNEL); + term = uvc_alloc_entity(type | UVC_TERM_OUTPUT, buffer[3], + 1, 0); if (term == NULL) return -ENOMEM; - term->id = buffer[3]; - term->type = type | UVC_TERM_OUTPUT; - term->output.bSourceID = buffer[7]; + memcpy(term->baSourceID, &buffer[7], 1); if (buffer[8] != 0) usb_string(udev, buffer[8], term->name, @@ -1026,15 +1031,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + p, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, 0); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; - unit->selector.bNrInPins = buffer[4]; - unit->selector.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->selector.baSourceID, &buffer[5], p); + memcpy(unit->baSourceID, &buffer[5], p); if (buffer[5+p] != 0) usb_string(udev, buffer[5+p], unit->name, @@ -1056,13 +1057,11 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + n, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], 2, n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; - unit->processing.bSourceID = buffer[4]; + memcpy(unit->baSourceID, &buffer[4], 1); unit->processing.wMaxMultiplier = get_unaligned_le16(&buffer[5]); unit->processing.bControlSize = buffer[7]; @@ -1091,19 +1090,15 @@ static int uvc_parse_standard_control(struct uvc_device *dev, return -EINVAL; } - unit = kzalloc(sizeof *unit + p + n, GFP_KERNEL); + unit = uvc_alloc_entity(buffer[2], buffer[3], p + 1, n); if (unit == NULL) return -ENOMEM; - unit->id = buffer[3]; - unit->type = buffer[2]; memcpy(unit->extension.guidExtensionCode, &buffer[4], 16); unit->extension.bNumControls = buffer[20]; - unit->extension.bNrInPins = get_unaligned_le16(&buffer[21]); - unit->extension.baSourceID = (__u8 *)unit + sizeof *unit; - memcpy(unit->extension.baSourceID, &buffer[22], p); + memcpy(unit->baSourceID, &buffer[22], p); unit->extension.bControlSize = buffer[22+p]; - unit->extension.bmControls = (__u8 *)unit + sizeof *unit + p; + unit->extension.bmControls = (__u8 *)unit + sizeof *unit; memcpy(unit->extension.bmControls, &buffer[23+p], n); if (buffer[23+p+n] != 0) @@ -1209,13 +1204,12 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- XU %d", entity->id); - if (entity->extension.bNrInPins != 1) { + if (entity->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d has more " "than 1 input pin.\n", entity->id); return -1; } - list_add_tail(&entity->chain, &chain->extensions); break; case UVC_VC_PROCESSING_UNIT: @@ -1236,7 +1230,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, printk(" <- SU %d", entity->id); /* Single-input selector units are ignored. */ - if (entity->selector.bNrInPins == 1) + if (entity->bNrInPins == 1) break; if (chain->selector != NULL) { @@ -1254,20 +1248,17 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" <- IT %d\n", entity->id); - list_add_tail(&entity->chain, &chain->iterms); break; case UVC_TT_STREAMING: - if (uvc_trace_param & UVC_TRACE_PROBE) - printk(" <- IT %d\n", entity->id); - - if (!UVC_ENTITY_IS_ITERM(entity)) { - uvc_trace(UVC_TRACE_DESCR, "Unsupported input " - "terminal %u.\n", entity->id); - return -1; + if (UVC_ENTITY_IS_ITERM(entity)) { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" <- IT %d\n", entity->id); + } else { + if (uvc_trace_param & UVC_TRACE_PROBE) + printk(" OT %d", entity->id); } - list_add_tail(&entity->chain, &chain->iterms); break; default: @@ -1276,6 +1267,7 @@ static int uvc_scan_chain_entity(struct uvc_video_chain *chain, return -1; } + list_add_tail(&entity->chain, &chain->entities); return 0; } @@ -1299,14 +1291,14 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, switch (UVC_ENTITY_TYPE(forward)) { case UVC_VC_EXTENSION_UNIT: - if (forward->extension.bNrInPins != 1) { + if (forward->bNrInPins != 1) { uvc_trace(UVC_TRACE_DESCR, "Extension unit %d " "has more than 1 input pin.\n", entity->id); return -EINVAL; } - list_add_tail(&forward->chain, &chain->extensions); + list_add_tail(&forward->chain, &chain->entities); if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); @@ -1326,7 +1318,7 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, return -EINVAL; } - list_add_tail(&forward->chain, &chain->oterms); + list_add_tail(&forward->chain, &chain->entities); if (uvc_trace_param & UVC_TRACE_PROBE) { if (!found) printk(" (->"); @@ -1344,24 +1336,22 @@ static int uvc_scan_chain_forward(struct uvc_video_chain *chain, } static int uvc_scan_chain_backward(struct uvc_video_chain *chain, - struct uvc_entity *entity) + struct uvc_entity **_entity) { + struct uvc_entity *entity = *_entity; struct uvc_entity *term; - int id = -1, i; + int id = -EINVAL, i; switch (UVC_ENTITY_TYPE(entity)) { case UVC_VC_EXTENSION_UNIT: - id = entity->extension.baSourceID[0]; - break; - case UVC_VC_PROCESSING_UNIT: - id = entity->processing.bSourceID; + id = entity->baSourceID[0]; break; case UVC_VC_SELECTOR_UNIT: /* Single-input selector units are ignored. */ - if (entity->selector.bNrInPins == 1) { - id = entity->selector.baSourceID[0]; + if (entity->bNrInPins == 1) { + id = entity->baSourceID[0]; break; } @@ -1369,8 +1359,8 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, printk(" <- IT"); chain->selector = entity; - for (i = 0; i < entity->selector.bNrInPins; ++i) { - id = entity->selector.baSourceID[i]; + for (i = 0; i < entity->bNrInPins; ++i) { + id = entity->baSourceID[i]; term = uvc_entity_by_id(chain->dev, id); if (term == NULL || !UVC_ENTITY_IS_ITERM(term)) { uvc_trace(UVC_TRACE_DESCR, "Selector unit %d " @@ -1382,7 +1372,7 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, if (uvc_trace_param & UVC_TRACE_PROBE) printk(" %d", term->id); - list_add_tail(&term->chain, &chain->iterms); + list_add_tail(&term->chain, &chain->entities); uvc_scan_chain_forward(chain, term, entity); } @@ -1391,34 +1381,49 @@ static int uvc_scan_chain_backward(struct uvc_video_chain *chain, id = 0; break; + + case UVC_ITT_VENDOR_SPECIFIC: + case UVC_ITT_CAMERA: + case UVC_ITT_MEDIA_TRANSPORT_INPUT: + case UVC_OTT_VENDOR_SPECIFIC: + case UVC_OTT_DISPLAY: + case UVC_OTT_MEDIA_TRANSPORT_OUTPUT: + case UVC_TT_STREAMING: + id = UVC_ENTITY_IS_OTERM(entity) ? entity->baSourceID[0] : 0; + break; + } + + if (id <= 0) { + *_entity = NULL; + return id; } - return id; + entity = uvc_entity_by_id(chain->dev, id); + if (entity == NULL) { + uvc_trace(UVC_TRACE_DESCR, "Found reference to " + "unknown entity %d.\n", id); + return -EINVAL; + } + + *_entity = entity; + return 0; } static int uvc_scan_chain(struct uvc_video_chain *chain, - struct uvc_entity *oterm) + struct uvc_entity *term) { struct uvc_entity *entity, *prev; - int id; - entity = oterm; - list_add_tail(&entity->chain, &chain->oterms); - uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain: OT %d", entity->id); + uvc_trace(UVC_TRACE_PROBE, "Scanning UVC chain:"); - id = entity->output.bSourceID; - while (id != 0) { - prev = entity; - entity = uvc_entity_by_id(chain->dev, id); - if (entity == NULL) { - uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "unknown entity %d.\n", id); - return -EINVAL; - } + entity = term; + prev = NULL; + while (entity != NULL) { + /* Entity must not be part of an existing chain */ if (entity->chain.next || entity->chain.prev) { uvc_trace(UVC_TRACE_DESCR, "Found reference to " - "entity %d already in chain.\n", id); + "entity %d already in chain.\n", entity->id); return -EINVAL; } @@ -1430,34 +1435,34 @@ static int uvc_scan_chain(struct uvc_video_chain *chain, if (uvc_scan_chain_forward(chain, entity, prev) < 0) return -EINVAL; - /* Stop when a terminal is found. */ - if (UVC_ENTITY_IS_TERM(entity)) - break; - /* Backward scan */ - id = uvc_scan_chain_backward(chain, entity); - if (id < 0) - return id; + prev = entity; + if (uvc_scan_chain_backward(chain, &entity) < 0) + return -EINVAL; } return 0; } -static unsigned int uvc_print_terms(struct list_head *terms, char *buffer) +static unsigned int uvc_print_terms(struct list_head *terms, u16 dir, + char *buffer) { struct uvc_entity *term; unsigned int nterms = 0; char *p = buffer; list_for_each_entry(term, terms, chain) { - p += sprintf(p, "%u", term->id); - if (term->chain.next != terms) { + if (!UVC_ENTITY_IS_TERM(term) || + UVC_TERM_DIRECTION(term) != dir) + continue; + + if (nterms) p += sprintf(p, ","); - if (++nterms >= 4) { - p += sprintf(p, "..."); - break; - } + if (++nterms >= 4) { + p += sprintf(p, "..."); + break; } + p += sprintf(p, "%u", term->id); } return p - buffer; @@ -1468,9 +1473,9 @@ static const char *uvc_print_chain(struct uvc_video_chain *chain) static char buffer[43]; char *p = buffer; - p += uvc_print_terms(&chain->iterms, p); + p += uvc_print_terms(&chain->entities, UVC_TERM_INPUT, p); p += sprintf(p, " -> "); - uvc_print_terms(&chain->oterms, p); + uvc_print_terms(&chain->entities, UVC_TERM_OUTPUT, p); return buffer; } @@ -1501,9 +1506,7 @@ static int uvc_scan_device(struct uvc_device *dev) if (chain == NULL) return -ENOMEM; - INIT_LIST_HEAD(&chain->iterms); - INIT_LIST_HEAD(&chain->oterms); - INIT_LIST_HEAD(&chain->extensions); + INIT_LIST_HEAD(&chain->entities); mutex_init(&chain->ctrl_mutex); chain->dev = dev; @@ -1530,6 +1533,66 @@ static int uvc_scan_device(struct uvc_device *dev) * Video device registration and unregistration */ +/* + * Delete the UVC device. + * + * Called by the kernel when the last reference to the uvc_device structure + * is released. + * + * As this function is called after or during disconnect(), all URBs have + * already been canceled by the USB core. There is no need to kill the + * interrupt URB manually. + */ +static void uvc_delete(struct uvc_device *dev) +{ + struct list_head *p, *n; + + usb_put_intf(dev->intf); + usb_put_dev(dev->udev); + + uvc_status_cleanup(dev); + uvc_ctrl_cleanup_device(dev); + + list_for_each_safe(p, n, &dev->chains) { + struct uvc_video_chain *chain; + chain = list_entry(p, struct uvc_video_chain, list); + kfree(chain); + } + + list_for_each_safe(p, n, &dev->entities) { + struct uvc_entity *entity; + entity = list_entry(p, struct uvc_entity, list); + kfree(entity); + } + + list_for_each_safe(p, n, &dev->streams) { + struct uvc_streaming *streaming; + streaming = list_entry(p, struct uvc_streaming, list); + usb_driver_release_interface(&uvc_driver.driver, + streaming->intf); + usb_put_intf(streaming->intf); + kfree(streaming->format); + kfree(streaming->header.bmaControls); + kfree(streaming); + } + + kfree(dev); +} + +static void uvc_release(struct video_device *vdev) +{ + struct uvc_streaming *stream = video_get_drvdata(vdev); + struct uvc_device *dev = stream->dev; + + video_device_release(vdev); + + /* Decrement the registered streams count and delete the device when it + * reaches zero. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); +} + /* * Unregister the video devices. */ @@ -1537,16 +1600,26 @@ static void uvc_unregister_video(struct uvc_device *dev) { struct uvc_streaming *stream; + /* Unregistering all video devices might result in uvc_delete() being + * called from inside the loop if there's no open file handle. To avoid + * that, increment the stream count before iterating over the streams + * and decrement it when done. + */ + atomic_inc(&dev->nstreams); + list_for_each_entry(stream, &dev->streams, list) { if (stream->vdev == NULL) continue; - if (stream->vdev->minor == -1) - video_device_release(stream->vdev); - else - video_unregister_device(stream->vdev); + video_unregister_device(stream->vdev); stream->vdev = NULL; } + + /* Decrement the stream count and call uvc_delete explicitly if there + * are no stream left. + */ + if (atomic_dec_and_test(&dev->nstreams)) + uvc_delete(dev); } static int uvc_register_video(struct uvc_device *dev, @@ -1580,7 +1653,7 @@ static int uvc_register_video(struct uvc_device *dev, vdev->parent = &dev->intf->dev; vdev->minor = -1; vdev->fops = &uvc_fops; - vdev->release = video_device_release; + vdev->release = uvc_release; strlcpy(vdev->name, dev->name, sizeof vdev->name); /* Set the driver data before calling video_register_device, otherwise @@ -1598,6 +1671,7 @@ static int uvc_register_video(struct uvc_device *dev, return ret; } + atomic_inc(&dev->nstreams); return 0; } @@ -1605,13 +1679,13 @@ static int uvc_register_video(struct uvc_device *dev, * Register all video devices in all chains. */ static int uvc_register_terms(struct uvc_device *dev, - struct uvc_video_chain *chain, struct list_head *terms) + struct uvc_video_chain *chain) { struct uvc_streaming *stream; struct uvc_entity *term; int ret; - list_for_each_entry(term, terms, chain) { + list_for_each_entry(term, &chain->entities, chain) { if (UVC_ENTITY_TYPE(term) != UVC_TT_STREAMING) continue; @@ -1637,11 +1711,7 @@ static int uvc_register_chains(struct uvc_device *dev) int ret; list_for_each_entry(chain, &dev->chains, list) { - ret = uvc_register_terms(dev, chain, &chain->iterms); - if (ret < 0) - return ret; - - ret = uvc_register_terms(dev, chain, &chain->oterms); + ret = uvc_register_terms(dev, chain); if (ret < 0) return ret; } @@ -1653,61 +1723,6 @@ static int uvc_register_chains(struct uvc_device *dev) * USB probe, disconnect, suspend and resume */ -/* - * Delete the UVC device. - * - * Called by the kernel when the last reference to the uvc_device structure - * is released. - * - * Unregistering the video devices is done here because every opened instance - * must be closed before the device can be unregistered. An alternative would - * have been to use another reference count for uvc_v4l2_open/uvc_release, and - * unregister the video devices on disconnect when that reference count drops - * to zero. - * - * As this function is called after or during disconnect(), all URBs have - * already been canceled by the USB core. There is no need to kill the - * interrupt URB manually. - */ -void uvc_delete(struct kref *kref) -{ - struct uvc_device *dev = container_of(kref, struct uvc_device, kref); - struct list_head *p, *n; - - /* Unregister the video devices. */ - uvc_unregister_video(dev); - usb_put_intf(dev->intf); - usb_put_dev(dev->udev); - - uvc_status_cleanup(dev); - uvc_ctrl_cleanup_device(dev); - - list_for_each_safe(p, n, &dev->chains) { - struct uvc_video_chain *chain; - chain = list_entry(p, struct uvc_video_chain, list); - kfree(chain); - } - - list_for_each_safe(p, n, &dev->entities) { - struct uvc_entity *entity; - entity = list_entry(p, struct uvc_entity, list); - kfree(entity); - } - - list_for_each_safe(p, n, &dev->streams) { - struct uvc_streaming *streaming; - streaming = list_entry(p, struct uvc_streaming, list); - usb_driver_release_interface(&uvc_driver.driver, - streaming->intf); - usb_put_intf(streaming->intf); - kfree(streaming->format); - kfree(streaming->header.bmaControls); - kfree(streaming); - } - - kfree(dev); -} - static int uvc_probe(struct usb_interface *intf, const struct usb_device_id *id) { @@ -1730,7 +1745,7 @@ static int uvc_probe(struct usb_interface *intf, INIT_LIST_HEAD(&dev->entities); INIT_LIST_HEAD(&dev->chains); INIT_LIST_HEAD(&dev->streams); - kref_init(&dev->kref); + atomic_set(&dev->nstreams, 0); atomic_set(&dev->users, 0); dev->udev = usb_get_dev(udev); @@ -1792,7 +1807,7 @@ static int uvc_probe(struct usb_interface *intf, return 0; error: - kref_put(&dev->kref, uvc_delete); + uvc_unregister_video(dev); return -ENODEV; } @@ -1809,21 +1824,9 @@ static void uvc_disconnect(struct usb_interface *intf) UVC_SC_VIDEOSTREAMING) return; - /* uvc_v4l2_open() might race uvc_disconnect(). A static driver-wide - * lock is needed to prevent uvc_disconnect from releasing its - * reference to the uvc_device instance after uvc_v4l2_open() received - * the pointer to the device (video_devdata) but before it got the - * chance to increase the reference count (kref_get). - * - * Note that the reference can't be released with the lock held, - * otherwise a AB-BA deadlock can occur with videodev_lock that - * videodev acquires in videodev_open() and video_unregister_device(). - */ - mutex_lock(&uvc_driver.open_mutex); dev->state |= UVC_DEV_DISCONNECTED; - mutex_unlock(&uvc_driver.open_mutex); - kref_put(&dev->kref, uvc_delete); + uvc_unregister_video(dev); } static int uvc_suspend(struct usb_interface *intf, pm_message_t message) @@ -1899,6 +1902,15 @@ static int uvc_reset_resume(struct usb_interface *intf) * though they are compliant. */ static struct usb_device_id uvc_ids[] = { + /* Genius eFace 2025 */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0458, + .idProduct = 0x706e, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* Microsoft Lifecam NX-6000 */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2123,6 +2135,15 @@ static struct usb_device_id uvc_ids[] = { .bInterfaceSubClass = 1, .bInterfaceProtocol = 0, .driver_info = UVC_QUIRK_STATUS_INTERVAL }, + /* MSI StarCam 370i */ + { .match_flags = USB_DEVICE_ID_MATCH_DEVICE + | USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x1b3b, + .idProduct = 0x2951, + .bInterfaceClass = USB_CLASS_VIDEO, + .bInterfaceSubClass = 1, + .bInterfaceProtocol = 0, + .driver_info = UVC_QUIRK_PROBE_MINMAX }, /* SiGma Micro USB Web Camera */ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE | USB_DEVICE_ID_MATCH_INT_INFO, @@ -2159,7 +2180,6 @@ static int __init uvc_init(void) INIT_LIST_HEAD(&uvc_driver.devices); INIT_LIST_HEAD(&uvc_driver.controls); - mutex_init(&uvc_driver.open_mutex); mutex_init(&uvc_driver.ctrl_mutex); uvc_ctrl_init(); @@ -2184,6 +2204,8 @@ module_param_named(quirks, uvc_quirks_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(quirks, "Forced device quirks"); module_param_named(trace, uvc_trace_param, uint, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(trace, "Trace level bitmask"); +module_param_named(timeout, uvc_timeout_param, uint, S_IRUGO|S_IWUSR); +MODULE_PARM_DESC(timeout, "Streaming control requests timeout"); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index a2bdd806efab1..23239a4adefe0 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -364,37 +364,30 @@ static int uvc_v4l2_set_streamparm(struct uvc_streaming *stream, * unprivileged state. Only a single instance can be in a privileged state at * a given time. Trying to perform an operation that requires privileges will * automatically acquire the required privileges if possible, or return -EBUSY - * otherwise. Privileges are dismissed when closing the instance. + * otherwise. Privileges are dismissed when closing the instance or when + * freeing the video buffers using VIDIOC_REQBUFS. * * Operations that require privileges are: * * - VIDIOC_S_INPUT * - VIDIOC_S_PARM * - VIDIOC_S_FMT - * - VIDIOC_TRY_FMT * - VIDIOC_REQBUFS */ static int uvc_acquire_privileges(struct uvc_fh *handle) { - int ret = 0; - /* Always succeed if the handle is already privileged. */ if (handle->state == UVC_HANDLE_ACTIVE) return 0; /* Check if the device already has a privileged handle. */ - mutex_lock(&uvc_driver.open_mutex); if (atomic_inc_return(&handle->stream->active) != 1) { atomic_dec(&handle->stream->active); - ret = -EBUSY; - goto done; + return -EBUSY; } handle->state = UVC_HANDLE_ACTIVE; - -done: - mutex_unlock(&uvc_driver.open_mutex); - return ret; + return 0; } static void uvc_dismiss_privileges(struct uvc_fh *handle) @@ -421,24 +414,20 @@ static int uvc_v4l2_open(struct file *file) int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); - mutex_lock(&uvc_driver.open_mutex); stream = video_drvdata(file); - if (stream->dev->state & UVC_DEV_DISCONNECTED) { - ret = -ENODEV; - goto done; - } + if (stream->dev->state & UVC_DEV_DISCONNECTED) + return -ENODEV; ret = usb_autopm_get_interface(stream->dev->intf); if (ret < 0) - goto done; + return ret; /* Create the device handle. */ handle = kzalloc(sizeof *handle, GFP_KERNEL); if (handle == NULL) { usb_autopm_put_interface(stream->dev->intf); - ret = -ENOMEM; - goto done; + return -ENOMEM; } if (atomic_inc_return(&stream->dev->users) == 1) { @@ -447,7 +436,7 @@ static int uvc_v4l2_open(struct file *file) usb_autopm_put_interface(stream->dev->intf); atomic_dec(&stream->dev->users); kfree(handle); - goto done; + return ret; } } @@ -456,11 +445,7 @@ static int uvc_v4l2_open(struct file *file) handle->state = UVC_HANDLE_PASSIVE; file->private_data = handle; - kref_get(&stream->dev->kref); - -done: - mutex_unlock(&uvc_driver.open_mutex); - return ret; + return 0; } static int uvc_v4l2_release(struct file *file) @@ -490,7 +475,6 @@ static int uvc_v4l2_release(struct file *file) uvc_status_stop(stream->dev); usb_autopm_put_interface(stream->dev->intf); - kref_put(&stream->dev->kref, uvc_delete); return 0; } @@ -636,12 +620,16 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) { if (index != 0) return -EINVAL; - iterm = list_first_entry(&chain->iterms, - struct uvc_entity, chain); + list_for_each_entry(iterm, &chain->entities, chain) { + if (UVC_ENTITY_IS_ITERM(iterm)) + break; + } pin = iterm->id; - } else if (pin < selector->selector.bNrInPins) { - pin = selector->selector.baSourceID[index]; - list_for_each_entry(iterm, chain->iterms.next, chain) { + } else if (pin < selector->bNrInPins) { + pin = selector->baSourceID[index]; + list_for_each_entry(iterm, &chain->entities, chain) { + if (!UVC_ENTITY_IS_ITERM(iterm)) + continue; if (iterm->id == pin) break; } @@ -692,7 +680,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } - if (input == 0 || input > chain->selector->selector.bNrInPins) + if (input == 0 || input > chain->selector->bNrInPins) return -EINVAL; return uvc_query_ctrl(chain->dev, UVC_SET_CUR, @@ -731,9 +719,6 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct uvc_streaming_control probe; - if ((ret = uvc_acquire_privileges(handle)) < 0) - return ret; - return uvc_v4l2_try_format(stream, arg, &probe, NULL, NULL); } @@ -888,6 +873,9 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (ret < 0) return ret; + if (ret == 0) + uvc_dismiss_privileges(handle); + rb->count = ret; ret = 0; break; @@ -1051,7 +1039,7 @@ static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); - return -ENODEV; + return -EINVAL; } /* diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c index a6e41d12b2217..05139a4f14f67 100644 --- a/drivers/media/video/uvc/uvc_video.c +++ b/drivers/media/video/uvc/uvc_video.c @@ -135,7 +135,7 @@ static int uvc_get_video_ctrl(struct uvc_streaming *stream, ret = __uvc_query_ctrl(stream->dev, query, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, UVC_CTRL_STREAMING_TIMEOUT); + size, uvc_timeout_param); if ((query == UVC_GET_MIN || query == UVC_GET_MAX) && ret == 2) { /* Some cameras, mostly based on Bison Electronics chipsets, @@ -239,7 +239,7 @@ static int uvc_set_video_ctrl(struct uvc_streaming *stream, ret = __uvc_query_ctrl(stream->dev, UVC_SET_CUR, 0, stream->intfnum, probe ? UVC_VS_PROBE_CONTROL : UVC_VS_COMMIT_CONTROL, data, - size, UVC_CTRL_STREAMING_TIMEOUT); + size, uvc_timeout_param); if (ret != size) { uvc_printk(KERN_ERR, "Failed to set UVC %s control : " "%d (exp. %u).\n", probe ? "probe" : "commit", @@ -770,8 +770,9 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, /* Retry allocations until one succeed. */ for (; npackets > 1; npackets /= 2) { for (i = 0; i < UVC_URBS; ++i) { + stream->urb_size = psize * npackets; stream->urb_buffer[i] = usb_buffer_alloc( - stream->dev->udev, psize * npackets, + stream->dev->udev, stream->urb_size, gfp_flags | __GFP_NOWARN, &stream->urb_dma[i]); if (!stream->urb_buffer[i]) { uvc_free_urb_buffers(stream); @@ -780,11 +781,15 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream, } if (i == UVC_URBS) { - stream->urb_size = psize * npackets; + uvc_trace(UVC_TRACE_VIDEO, "Allocated %u URB buffers " + "of %ux%u bytes each.\n", UVC_URBS, npackets, + psize); return npackets; } } + uvc_trace(UVC_TRACE_VIDEO, "Failed to allocate URB buffers (%u bytes " + "per packet).\n", psize); return 0; } @@ -935,10 +940,12 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) bandwidth = stream->ctrl.dwMaxPayloadTransferSize; if (bandwidth == 0) { - uvc_printk(KERN_WARNING, "device %s requested null " - "bandwidth, defaulting to lowest.\n", - stream->dev->name); + uvc_trace(UVC_TRACE_VIDEO, "Device requested null " + "bandwidth, defaulting to lowest.\n"); bandwidth = 1; + } else { + uvc_trace(UVC_TRACE_VIDEO, "Device requested %u " + "B/frame bandwidth.\n", bandwidth); } for (i = 0; i < intf->num_altsetting; ++i) { @@ -955,8 +962,11 @@ static int uvc_init_video(struct uvc_streaming *stream, gfp_t gfp_flags) break; } - if (i >= intf->num_altsetting) + if (i >= intf->num_altsetting) { + uvc_trace(UVC_TRACE_VIDEO, "No fast enough alt setting " + "for requested bandwidth.\n"); return -EIO; + } ret = usb_set_interface(stream->dev->udev, intfnum, i); if (ret < 0) diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index e7958aa454cef..7ec9a04ced505 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -75,6 +75,7 @@ struct uvc_xu_control { #define UVC_TERM_INPUT 0x0000 #define UVC_TERM_OUTPUT 0x8000 +#define UVC_TERM_DIRECTION(term) ((term)->type & 0x8000) #define UVC_ENTITY_TYPE(entity) ((entity)->type & 0x7fff) #define UVC_ENTITY_IS_UNIT(entity) (((entity)->type & 0xff00) == 0) @@ -148,7 +149,7 @@ struct uvc_xu_control { #define UVC_MAX_STATUS_SIZE 16 #define UVC_CTRL_CONTROL_TIMEOUT 300 -#define UVC_CTRL_STREAMING_TIMEOUT 1000 +#define UVC_CTRL_STREAMING_TIMEOUT 3000 /* Devices quirks */ #define UVC_QUIRK_STATUS_INTERVAL 0x00000001 @@ -292,11 +293,9 @@ struct uvc_entity { } media; struct { - __u8 bSourceID; } output; struct { - __u8 bSourceID; __u16 wMaxMultiplier; __u8 bControlSize; __u8 *bmControls; @@ -304,21 +303,20 @@ struct uvc_entity { } processing; struct { - __u8 bNrInPins; - __u8 *baSourceID; } selector; struct { __u8 guidExtensionCode[16]; __u8 bNumControls; - __u8 bNrInPins; - __u8 *baSourceID; __u8 bControlSize; __u8 *bmControls; __u8 *bmControlsType; } extension; }; + __u8 bNrInPins; + __u8 *baSourceID; + unsigned int ncontrols; struct uvc_control *controls; }; @@ -408,11 +406,9 @@ struct uvc_video_chain { struct uvc_device *dev; struct list_head list; - struct list_head iterms; /* Input terminals */ - struct list_head oterms; /* Output terminals */ + struct list_head entities; /* All entities */ struct uvc_entity *processing; /* Processing unit */ struct uvc_entity *selector; /* Selector unit */ - struct list_head extensions; /* Extension units */ struct mutex ctrl_mutex; }; @@ -475,7 +471,6 @@ struct uvc_device { char name[32]; enum uvc_device_state state; - struct kref kref; struct list_head list; atomic_t users; @@ -488,6 +483,7 @@ struct uvc_device { /* Video Streaming interfaces */ struct list_head streams; + atomic_t nstreams; /* Status Interrupt Endpoint */ struct usb_host_endpoint *int_ep; @@ -511,8 +507,6 @@ struct uvc_fh { struct uvc_driver { struct usb_driver driver; - struct mutex open_mutex; /* protects from open/disconnect race */ - struct list_head devices; /* struct uvc_device list */ struct list_head controls; /* struct uvc_control_info list */ struct mutex ctrl_mutex; /* protects controls and devices @@ -533,12 +527,14 @@ struct uvc_driver { #define UVC_TRACE_FRAME (1 << 7) #define UVC_TRACE_SUSPEND (1 << 8) #define UVC_TRACE_STATUS (1 << 9) +#define UVC_TRACE_VIDEO (1 << 10) #define UVC_WARN_MINMAX 0 #define UVC_WARN_PROBE_DEF 1 extern unsigned int uvc_no_drop_param; extern unsigned int uvc_trace_param; +extern unsigned int uvc_timeout_param; #define uvc_trace(flag, msg...) \ do { \ @@ -571,7 +567,6 @@ extern unsigned int uvc_trace_param; /* Core driver */ extern struct uvc_driver uvc_driver; -extern void uvc_delete(struct kref *kref); /* Video buffers queue management. */ extern void uvc_queue_init(struct uvc_video_queue *queue, diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c index f5a93ae3cdf9d..e8e5affbabced 100644 --- a/drivers/media/video/v4l2-common.c +++ b/drivers/media/video/v4l2-common.c @@ -431,6 +431,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_CHROMA_AGC: return "Chroma AGC"; case V4L2_CID_COLOR_KILLER: return "Color Killer"; case V4L2_CID_COLORFX: return "Color Effects"; + case V4L2_CID_ROTATE: return "Rotate"; + case V4L2_CID_BG_COLOR: return "Background color"; /* MPEG controls */ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls"; @@ -587,6 +589,13 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; min = max = step = def = 0; break; + case V4L2_CID_BG_COLOR: + qctrl->type = V4L2_CTRL_TYPE_INTEGER; + step = 1; + min = 0; + /* Max is calculated as RGB888 that is 2^24 */ + max = 0xFFFFFF; + break; default: qctrl->type = V4L2_CTRL_TYPE_INTEGER; break; diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index 8e93c6f25c83a..bb0a1c8de414b 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -110,7 +110,7 @@ EXPORT_SYMBOL_GPL(videobuf_queue_to_vmalloc); void videobuf_queue_core_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, @@ -360,7 +360,7 @@ int __videobuf_mmap_setup(struct videobuf_queue *q, q->bufs[i]->bsize = bsize; switch (memory) { case V4L2_MEMORY_MMAP: - q->bufs[i]->boff = bsize * i; + q->bufs[i]->boff = PAGE_ALIGN(bsize) * i; break; case V4L2_MEMORY_USERPTR: case V4L2_MEMORY_OVERLAY: @@ -430,9 +430,9 @@ int videobuf_reqbufs(struct videobuf_queue *q, count = VIDEO_MAX_FRAME; size = 0; q->ops->buf_setup(q, &count, &size); - size = PAGE_ALIGN(size); - dprintk(1, "reqbufs: bufs=%d, size=0x%x [%d pages total]\n", - count, size, (count*size)>>PAGE_SHIFT); + dprintk(1, "reqbufs: bufs=%d, size=0x%x [%u pages total]\n", + count, size, + (unsigned int)((count*PAGE_ALIGN(size))>>PAGE_SHIFT) ); retval = __videobuf_mmap_setup(q, count, size, req->memory); if (retval < 0) { @@ -1099,7 +1099,7 @@ int videobuf_cgmbuf(struct videobuf_queue *q, mbuf->size = 0; for (i = 0; i < mbuf->frames; i++) { mbuf->offsets[i] = q->bufs[i]->boff; - mbuf->size += q->bufs[i]->bsize; + mbuf->size += PAGE_ALIGN(q->bufs[i]->bsize); } return 0; diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index c3065c4bcba9f..d25f28461da1f 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -429,7 +429,7 @@ static struct videobuf_qtype_ops qops = { }; void videobuf_queue_dma_contig_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/drivers/media/video/videobuf-dma-sg.c b/drivers/media/video/videobuf-dma-sg.c index 032ebae0134a7..fa78555b118bd 100644 --- a/drivers/media/video/videobuf-dma-sg.c +++ b/drivers/media/video/videobuf-dma-sg.c @@ -588,7 +588,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, retval = -EBUSY; goto done; } - size += q->bufs[last]->bsize; + size += PAGE_ALIGN(q->bufs[last]->bsize); if (size == (vma->vm_end - vma->vm_start)) break; } @@ -610,7 +610,7 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, continue; q->bufs[i]->map = map; q->bufs[i]->baddr = vma->vm_start + size; - size += q->bufs[i]->bsize; + size += PAGE_ALIGN(q->bufs[i]->bsize); } map->count = 1; @@ -702,7 +702,7 @@ void *videobuf_sg_alloc(size_t size) } void videobuf_queue_sg_init(struct videobuf_queue* q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 0e7dcba8e4ae0..a56cf0d3a6d61 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -139,7 +139,9 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe, struct device *device, char *adapter_name, short *adapter_nr, - int mfe_shared) + int mfe_shared, + int (*fe_ioctl_override)(struct dvb_frontend *, + unsigned int, void *, unsigned int)) { int result; @@ -154,6 +156,7 @@ static int videobuf_dvb_register_adapter(struct videobuf_dvb_frontends *fe, } fe->adapter.priv = adapter_priv; fe->adapter.mfe_shared = mfe_shared; + fe->adapter.fe_ioctl_override = fe_ioctl_override; return result; } @@ -253,7 +256,9 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, void *adapter_priv, struct device *device, short *adapter_nr, - int mfe_shared) + int mfe_shared, + int (*fe_ioctl_override)(struct dvb_frontend *, + unsigned int, void *, unsigned int)) { struct list_head *list, *q; struct videobuf_dvb_frontend *fe; @@ -267,7 +272,7 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, /* Bring up the adapter */ res = videobuf_dvb_register_adapter(f, module, adapter_priv, device, - fe->dvb.name, adapter_nr, mfe_shared); + fe->dvb.name, adapter_nr, mfe_shared, fe_ioctl_override); if (res < 0) { printk(KERN_WARNING "videobuf_dvb_register_adapter failed (errno = %d)\n", res); return res; diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c index 35f3900c56332..d6e6a28fb6b89 100644 --- a/drivers/media/video/videobuf-vmalloc.c +++ b/drivers/media/video/videobuf-vmalloc.c @@ -391,8 +391,8 @@ static struct videobuf_qtype_ops qops = { }; void videobuf_queue_vmalloc_init(struct videobuf_queue* q, - struct videobuf_queue_ops *ops, - void *dev, + const struct videobuf_queue_ops *ops, + struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field, diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c index 97e0ce28ff18f..33205d7537d8f 100644 --- a/drivers/media/video/vpx3220.c +++ b/drivers/media/video/vpx3220.c @@ -391,7 +391,7 @@ static int vpx3220_s_routing(struct v4l2_subdev *sd, {0x0e, 1} }; - if (input < 0 || input > 2) + if (input > 2) return -EINVAL; v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[input]); diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 47137deafcfd9..e9f72ca458f1b 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -2764,7 +2764,7 @@ static int zoran_enum_input(struct file *file, void *__fh, struct zoran_fh *fh = __fh; struct zoran *zr = fh->zr; - if (inp->index < 0 || inp->index >= zr->card.inputs) + if (inp->index >= zr->card.inputs) return -EINVAL; else { int id = inp->index; diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index 9aae011d92abb..2ef110b5221bd 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -115,6 +115,7 @@ static struct usb_device_id device_table[] = { {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 }, + {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, {} /* Terminating entry */ }; diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/go7007/Makefile index 1301caa7495dd..d37b7edc27937 100644 --- a/drivers/staging/go7007/Makefile +++ b/drivers/staging/go7007/Makefile @@ -5,7 +5,7 @@ obj-$(CONFIG_VIDEO_GO7007) += go7007.o obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o -obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o +obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o @@ -17,7 +17,7 @@ obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ snd-go7007.o -s2250-objs += s2250-board.o s2250-loader.o +s2250-objs += s2250-board.o # Uncomment when the saa7134 patches get into upstream #ifneq ($(CONFIG_VIDEO_SAA7134),) diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/go7007/go7007-driver.c index 472f4bb08fdcd..fb1345ffb8588 100644 --- a/drivers/staging/go7007/go7007-driver.c +++ b/drivers/staging/go7007/go7007-driver.c @@ -49,7 +49,7 @@ int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data) go->hpi_ops->read_interrupt(go); if (wait_event_timeout(go->interrupt_waitq, go->interrupt_available, 5*HZ) < 0) { - v4l2_err(go->video_dev, "timeout waiting for read interrupt\n"); + v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n"); return -1; } if (!go->interrupt_available) @@ -193,7 +193,8 @@ int go7007_reset_encoder(struct go7007 *go) static int init_i2c_module(struct i2c_adapter *adapter, const char *type, int id, int addr) { - struct i2c_board_info info; + struct go7007 *go = i2c_get_adapdata(adapter); + struct v4l2_device *v4l2_dev = &go->v4l2_dev; char *modname; switch (id) { @@ -219,20 +220,16 @@ static int init_i2c_module(struct i2c_adapter *adapter, const char *type, modname = "wis-ov7640"; break; case I2C_DRIVERID_S2250: - modname = "s2250-board"; + modname = "s2250"; break; default: modname = NULL; break; } - if (modname != NULL) - request_module(modname); - memset(&info, 0, sizeof(struct i2c_board_info)); - info.addr = addr; - strlcpy(info.type, type, I2C_NAME_SIZE); - if (!i2c_new_device(adapter, &info)) + if (v4l2_i2c_new_subdev(v4l2_dev, adapter, modname, type, addr, NULL)) return 0; + if (modname != NULL) printk(KERN_INFO "go7007: probing for module %s failed\n", modname); @@ -262,6 +259,11 @@ int go7007_register_encoder(struct go7007 *go) if (ret < 0) return -1; + /* v4l2 init must happen before i2c subdevs */ + ret = go7007_v4l2_init(go); + if (ret < 0) + return ret; + if (!go->i2c_adapter_online && go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { if (go7007_i2c_init(go) < 0) @@ -282,7 +284,7 @@ int go7007_register_encoder(struct go7007 *go) go->audio_enabled = 1; go7007_snd_init(go); } - return go7007_v4l2_init(go); + return 0; } EXPORT_SYMBOL(go7007_register_encoder); @@ -315,7 +317,7 @@ int go7007_start_encoder(struct go7007 *go) if (go7007_send_firmware(go, fw, fw_len) < 0 || go7007_read_interrupt(go, &intr_val, &intr_data) < 0) { - v4l2_err(go->video_dev, "error transferring firmware\n"); + v4l2_err(&go->v4l2_dev, "error transferring firmware\n"); rv = -1; goto start_error; } @@ -324,7 +326,7 @@ int go7007_start_encoder(struct go7007 *go) go->parse_length = 0; go->seen_frame = 0; if (go7007_stream_start(go) < 0) { - v4l2_err(go->video_dev, "error starting stream transfer\n"); + v4l2_err(&go->v4l2_dev, "error starting stream transfer\n"); rv = -1; goto start_error; } @@ -420,7 +422,7 @@ void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) for (i = 0; i < length; ++i) { if (go->active_buf != NULL && go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) { - v4l2_info(go->video_dev, "dropping oversized frame\n"); + v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); go->active_buf->offset -= go->active_buf->bytesused; go->active_buf->bytesused = 0; go->active_buf->modet_active = 0; @@ -668,7 +670,7 @@ void go7007_remove(struct go7007 *go) if (i2c_del_adapter(&go->i2c_adapter) == 0) go->i2c_adapter_online = 0; else - v4l2_err(go->video_dev, + v4l2_err(&go->v4l2_dev, "error removing I2C adapter!\n"); } diff --git a/drivers/staging/go7007/go7007-priv.h b/drivers/staging/go7007/go7007-priv.h index ce9307e3e186d..b58c394c65552 100644 --- a/drivers/staging/go7007/go7007-priv.h +++ b/drivers/staging/go7007/go7007-priv.h @@ -21,6 +21,8 @@ * user-space applications. */ +#include + struct go7007; /* IDs to activate board-specific support code */ @@ -167,6 +169,7 @@ struct go7007 { int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */ char name[64]; struct video_device *video_dev; + struct v4l2_device v4l2_dev; int ref_count; enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status; spinlock_t spinlock; @@ -240,6 +243,11 @@ struct go7007 { unsigned short interrupt_data; }; +static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct go7007, v4l2_dev); +} + /* All of these must be called with the hpi_lock mutex held! */ #define go7007_interface_reset(go) \ ((go)->hpi_ops->interface_reset(go)) diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/go7007/go7007-usb.c index ecaa3c989cf45..1e89dc04ec23a 100644 --- a/drivers/staging/go7007/go7007-usb.c +++ b/drivers/staging/go7007/go7007-usb.c @@ -425,7 +425,7 @@ static struct go7007_usb_board board_sensoray_2250 = { .num_i2c_devs = 1, .i2c_devs = { { - .type = "s2250_board", + .type = "s2250", .id = I2C_DRIVERID_S2250, .addr = 0x43, }, @@ -1057,7 +1057,7 @@ static int go7007_usb_probe(struct usb_interface *intf, usb_rcvintpipe(usb->usbdev, 4), usb->intr_urb->transfer_buffer, 2*sizeof(u16), go7007_usb_readinterrupt_complete, go, 8); - usb_set_intfdata(intf, go); + usb_set_intfdata(intf, &go->v4l2_dev); /* Boot the GO7007 */ if (go7007_boot_encoder(go, go->board_info->flags & @@ -1233,7 +1233,7 @@ static int go7007_usb_probe(struct usb_interface *intf, static void go7007_usb_disconnect(struct usb_interface *intf) { - struct go7007 *go = usb_get_intfdata(intf); + struct go7007 *go = to_go7007(usb_get_intfdata(intf)); struct go7007_usb *usb = go->hpi_context; struct urb *vurb, *aurb; int i; diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c index 4bd353afa596a..b18d8e2d4c5e7 100644 --- a/drivers/staging/go7007/go7007-v4l2.c +++ b/drivers/staging/go7007/go7007-v4l2.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,9 @@ #define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3 #endif +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(dev, 0, o, f, ##args) + static void deactivate_buffer(struct go7007_buffer *gobuf) { int i; @@ -247,19 +251,23 @@ static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) go->modet_map[i] = 0; if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { - struct video_decoder_resolution res; + struct v4l2_format res; + + if (fmt != NULL) { + res = *fmt; + } else { + res.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + res.fmt.pix.width = width; + } - res.width = width; if (height > sensor_height / 2) { - res.height = height / 2; + res.fmt.pix.height = height / 2; go->encoder_v_halve = 0; } else { - res.height = height; + res.fmt.pix.height = height; go->encoder_v_halve = 1; } - if (go->i2c_adapter_online) - i2c_clients_command(&go->i2c_adapter, - DECODER_SET_RESOLUTION, &res); + call_all(&go->v4l2_dev, video, s_fmt, &res); } else { if (width <= sensor_width / 4) { go->encoder_h_halve = 1; @@ -385,7 +393,7 @@ static int clip_to_modet_map(struct go7007 *go, int region, } #endif -static int mpeg_queryctrl(struct v4l2_queryctrl *ctrl) +static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl) { static const u32 mpeg_ctrls[] = { V4L2_CID_MPEG_CLASS, @@ -973,51 +981,35 @@ static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *query) { struct go7007 *go = ((struct go7007_file *) priv)->go; + int id = query->id; - if (!go->i2c_adapter_online) - return -EIO; - - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, query); + if (0 == call_all(&go->v4l2_dev, core, queryctrl, query)) + return 0; - return (!query->name[0]) ? mpeg_queryctrl(query) : 0; + query->id = id; + return mpeg_query_ctrl(query); } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct go7007 *go = ((struct go7007_file *) priv)->go; - struct v4l2_queryctrl query; - if (!go->i2c_adapter_online) - return -EIO; - - memset(&query, 0, sizeof(query)); - query.id = ctrl->id; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); - if (query.name[0] == 0) - return mpeg_g_ctrl(ctrl, go); - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, ctrl); + if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl)) + return 0; - return 0; + return mpeg_g_ctrl(ctrl, go); } static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { struct go7007 *go = ((struct go7007_file *) priv)->go; - struct v4l2_queryctrl query; - if (!go->i2c_adapter_online) - return -EIO; - - memset(&query, 0, sizeof(query)); - query.id = ctrl->id; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); - if (query.name[0] == 0) - return mpeg_s_ctrl(ctrl, go); - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, ctrl); + if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl)) + return 0; - return 0; + return mpeg_s_ctrl(ctrl, go); } static int vidioc_g_parm(struct file *filp, void *priv, @@ -1135,8 +1127,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) if (go->streaming) return -EBUSY; - if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && - *std != 0) + if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0) return -EINVAL; if (*std == 0) @@ -1146,9 +1137,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) go->input == go->board_info->num_inputs - 1) { if (!go->i2c_adapter_online) return -EIO; - i2c_clients_command(&go->i2c_adapter, - VIDIOC_S_STD, std); - if (!*std) /* hack to indicate EINVAL from tuner */ + if (call_all(&go->v4l2_dev, core, s_std, *std) < 0) return -EINVAL; } @@ -1164,9 +1153,7 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) } else return -EINVAL; - if (go->i2c_adapter_online) - i2c_clients_command(&go->i2c_adapter, - VIDIOC_S_STD, std); + call_all(&go->v4l2_dev, core, s_std, *std); set_capture_size(go, NULL, 0); return 0; @@ -1180,7 +1167,7 @@ static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) go->input == go->board_info->num_inputs - 1) { if (!go->i2c_adapter_online) return -EIO; - i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYSTD, std); + return call_all(&go->v4l2_dev, video, querystd, std); } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) *std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; else @@ -1238,14 +1225,8 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int input) return -EBUSY; go->input = input; - if (go->i2c_adapter_online) { - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_INPUT, - &go->board_info->inputs[input].video_input); - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO, - &go->board_info->inputs[input].audio_input); - } - return 0; + return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0); } static int vidioc_g_tuner(struct file *file, void *priv, @@ -1260,10 +1241,7 @@ static int vidioc_g_tuner(struct file *file, void *priv, if (!go->i2c_adapter_online) return -EIO; - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, t); - - t->index = 0; - return 0; + return call_all(&go->v4l2_dev, tuner, g_tuner, t); } static int vidioc_s_tuner(struct file *file, void *priv, @@ -1287,9 +1265,7 @@ static int vidioc_s_tuner(struct file *file, void *priv, break; } - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, t); - - return 0; + return call_all(&go->v4l2_dev, tuner, s_tuner, t); } static int vidioc_g_frequency(struct file *file, void *priv, @@ -1303,8 +1279,8 @@ static int vidioc_g_frequency(struct file *file, void *priv, return -EIO; f->type = V4L2_TUNER_ANALOG_TV; - i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, f); - return 0; + + return call_all(&go->v4l2_dev, tuner, g_frequency, f); } static int vidioc_s_frequency(struct file *file, void *priv, @@ -1317,9 +1293,7 @@ static int vidioc_s_frequency(struct file *file, void *priv, if (!go->i2c_adapter_online) return -EIO; - i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, f); - - return 0; + return call_all(&go->v4l2_dev, tuner, s_frequency, f); } static int vidioc_cropcap(struct file *file, void *priv, @@ -1827,7 +1801,7 @@ int go7007_v4l2_init(struct go7007 *go) go->video_dev = video_device_alloc(); if (go->video_dev == NULL) return -ENOMEM; - memcpy(go->video_dev, &go7007_template, sizeof(go7007_template)); + *go->video_dev = go7007_template; go->video_dev->parent = go->dev; rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1); if (rv < 0) { @@ -1835,6 +1809,12 @@ int go7007_v4l2_init(struct go7007 *go) go->video_dev = NULL; return rv; } + rv = v4l2_device_register(go->dev, &go->v4l2_dev); + if (rv < 0) { + video_device_release(go->video_dev); + go->video_dev = NULL; + return rv; + } video_set_drvdata(go->video_dev, go); ++go->ref_count; printk(KERN_INFO "%s: registered device video%d [v4l2]\n", @@ -1858,4 +1838,5 @@ void go7007_v4l2_remove(struct go7007 *go) mutex_unlock(&go->hw_lock); if (go->video_dev) video_unregister_device(go->video_dev); + v4l2_device_unregister(&go->v4l2_dev); } diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c index f4a6541c3e60f..8cf7f2750b3fb 100644 --- a/drivers/staging/go7007/s2250-board.c +++ b/drivers/staging/go7007/s2250-board.c @@ -20,10 +20,14 @@ #include #include #include +#include #include -#include "s2250-loader.h" +#include +#include #include "go7007-priv.h" -#include "wis-i2c.h" + +MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver"); +MODULE_LICENSE("GPL v2"); #define TLV320_ADDRESS 0x34 #define VPX322_ADDR_ANALOGCONTROL1 0x02 @@ -112,6 +116,7 @@ static u16 vid_regs_fp_pal[] = }; struct s2250 { + struct v4l2_subdev sd; v4l2_std_id std; int input; int brightness; @@ -123,6 +128,11 @@ struct s2250 { struct i2c_client *audio; }; +static inline struct s2250 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s2250, sd); +} + /* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ static int go7007_usb_vendor_request(struct go7007 *go, u16 request, u16 value, u16 index, void *transfer_buffer, int length, int in) @@ -306,253 +316,262 @@ static int write_regs_fp(struct i2c_client *client, u16 *regs) } -static int s2250_command(struct i2c_client *client, - unsigned int cmd, void *arg) +/* ------------------------------------------------------------------------- */ + +static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) { - struct s2250 *dec = i2c_get_clientdata(client); + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int vidsys; + + vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00; + if (input == 0) { + /* composite */ + write_reg_fp(client, 0x20, 0x020 | vidsys); + write_reg_fp(client, 0x21, 0x662); + write_reg_fp(client, 0x140, 0x060); + } else if (input == 1) { + /* S-Video */ + write_reg_fp(client, 0x20, 0x040 | vidsys); + write_reg_fp(client, 0x21, 0x666); + write_reg_fp(client, 0x140, 0x060); + } else { + return -EINVAL; + } + state->input = input; + return 0; +} - switch (cmd) { - case VIDIOC_S_INPUT: - { - int vidsys; - int *input = arg; - - vidsys = (dec->std == V4L2_STD_NTSC) ? 0x01 : 0x00; - if (*input == 0) { - /* composite */ - write_reg_fp(client, 0x20, 0x020 | vidsys); - write_reg_fp(client, 0x21, 0x662); - write_reg_fp(client, 0x140, 0x060); - } else { - /* S-Video */ - write_reg_fp(client, 0x20, 0x040 | vidsys); - write_reg_fp(client, 0x21, 0x666); - write_reg_fp(client, 0x140, 0x060); - } - dec->input = *input; +static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 vidsource; + + vidsource = (state->input == 1) ? 0x040 : 0x020; + switch (norm) { + case V4L2_STD_NTSC: + write_regs_fp(client, vid_regs_fp); + write_reg_fp(client, 0x20, vidsource | 1); break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *std = arg; - u16 vidsource; - - vidsource = (dec->input == 1) ? 0x040 : 0x020; - dec->std = *std; - switch (dec->std) { - case V4L2_STD_NTSC: - write_regs_fp(client, vid_regs_fp); - write_reg_fp(client, 0x20, vidsource | 1); - break; - case V4L2_STD_PAL: - write_regs_fp(client, vid_regs_fp); - write_regs_fp(client, vid_regs_fp_pal); - write_reg_fp(client, 0x20, vidsource); - break; - default: - return -EINVAL; - } + case V4L2_STD_PAL: + write_regs_fp(client, vid_regs_fp); + write_regs_fp(client, vid_regs_fp_pal); + write_reg_fp(client, 0x20, vidsource); break; + default: + return -EINVAL; } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - static const u32 user_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - 0 - }; - static const u32 *ctrl_classes[] = { - user_ctrls, - NULL - }; - - ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); - break; - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); - break; - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); - break; - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0); - break; - default: - ctrl->name[0] = '\0'; - return -EINVAL; - } - break; + state->std = norm; + return 0; +} + +static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query) +{ + switch (query->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(query, -50, 50, 1, 0); + default: + return -EINVAL; } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - int value1; - u16 oldvalue; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 100) - dec->brightness = 100; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - value1 = (dec->brightness - 50) * 255 / 100; - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, - value1 | (oldvalue & ~0xff)); - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, - value1 | (oldvalue & ~0xff)); - write_reg_fp(client, 0x140, 0x60); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 100) - dec->contrast = 100; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - value1 = dec->contrast * 0x40 / 100; - if (value1 > 0x3f) - value1 = 0x3f; /* max */ - read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST0, - value1 | (oldvalue & ~0x3f)); - read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST1, - value1 | (oldvalue & ~0x3f)); - write_reg_fp(client, 0x140, 0x60); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 127) - dec->saturation = 127; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - - value1 = dec->saturation * 4140 / 100; - if (value1 > 4094) - value1 = 4094; - write_reg_fp(client, VPX322_ADDR_SAT, value1); - break; - case V4L2_CID_HUE: - if (ctrl->value > 50) - dec->hue = 50; - else if (ctrl->value < -50) - dec->hue = -50; - else - dec->hue = ctrl->value; - /* clamp the hue range */ - value1 = dec->hue * 280 / 50; - write_reg_fp(client, VPX322_ADDR_HUE, value1); - break; - } + return 0; +} + +static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int value1; + u16 oldvalue; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value > 100) + state->brightness = 100; + else if (ctrl->value < 0) + state->brightness = 0; + else + state->brightness = ctrl->value; + value1 = (state->brightness - 50) * 255 / 100; + read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); + write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, + value1 | (oldvalue & ~0xff)); + read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); + write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, + value1 | (oldvalue & ~0xff)); + write_reg_fp(client, 0x140, 0x60); break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } + case V4L2_CID_CONTRAST: + if (ctrl->value > 100) + state->contrast = 100; + else if (ctrl->value < 0) + state->contrast = 0; + else + state->contrast = ctrl->value; + value1 = state->contrast * 0x40 / 100; + if (value1 > 0x3f) + value1 = 0x3f; /* max */ + read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); + write_reg_fp(client, VPX322_ADDR_CONTRAST0, + value1 | (oldvalue & ~0x3f)); + read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); + write_reg_fp(client, VPX322_ADDR_CONTRAST1, + value1 | (oldvalue & ~0x3f)); + write_reg_fp(client, 0x140, 0x60); break; + case V4L2_CID_SATURATION: + if (ctrl->value > 100) + state->saturation = 100; + else if (ctrl->value < 0) + state->saturation = 0; + else + state->saturation = ctrl->value; + value1 = state->saturation * 4140 / 100; + if (value1 > 4094) + value1 = 4094; + write_reg_fp(client, VPX322_ADDR_SAT, value1); + break; + case V4L2_CID_HUE: + if (ctrl->value > 50) + state->hue = 50; + else if (ctrl->value < -50) + state->hue = -50; + else + state->hue = ctrl->value; + /* clamp the hue range */ + value1 = state->hue * 280 / 50; + write_reg_fp(client, VPX322_ADDR_HUE, value1); + break; + default: + return -EINVAL; } - case VIDIOC_S_FMT: - { - struct v4l2_format *fmt = arg; - if (fmt->fmt.pix.height < 640) { - write_reg_fp(client, 0x12b, dec->reg12b_val | 0x400); - write_reg_fp(client, 0x140, 0x060); - } else { - write_reg_fp(client, 0x12b, dec->reg12b_val & ~0x400); - write_reg_fp(client, 0x140, 0x060); - } - return 0; - } - case VIDIOC_G_AUDIO: - { - struct v4l2_audio *audio = arg; + return 0; +} - memset(audio, 0, sizeof(*audio)); - audio->index = dec->audio_input; - /* fall through */ - } - case VIDIOC_ENUMAUDIO: - { - struct v4l2_audio *audio = arg; - - switch (audio->index) { - case 0: - strcpy(audio->name, "Line In"); - break; - case 1: - strcpy(audio->name, "Mic"); - break; - case 2: - strcpy(audio->name, "Mic Boost"); - break; - default: - audio->name[0] = '\0'; - return 0; - } - audio->capability = V4L2_AUDCAP_STEREO; - audio->mode = 0; - return 0; +static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct s2250 *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = state->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + default: + return -EINVAL; } - case VIDIOC_S_AUDIO: - { - struct v4l2_audio *audio = arg; - - switch (audio->index) { - case 0: - write_reg(dec->audio, 0x08, 0x02); /* Line In */ - break; - case 1: - write_reg(dec->audio, 0x08, 0x04); /* Mic */ - break; - case 2: - write_reg(dec->audio, 0x08, 0x05); /* Mic Boost */ - break; - default: - return -EINVAL; - } - dec->audio_input = audio->index; - return 0; + return 0; +} + +static int s2250_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (fmt->fmt.pix.height < 640) { + write_reg_fp(client, 0x12b, state->reg12b_val | 0x400); + write_reg_fp(client, 0x140, 0x060); + } else { + write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400); + write_reg_fp(client, 0x140, 0x060); } + return 0; +} - default: - printk(KERN_INFO "s2250: unknown command 0x%x\n", cmd); +static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct s2250 *state = to_state(sd); + + switch (input) { + case 0: + write_reg(state->audio, 0x08, 0x02); /* Line In */ + break; + case 1: + write_reg(state->audio, 0x08, 0x04); /* Mic */ break; + case 2: + write_reg(state->audio, 0x08, 0x05); /* Mic Boost */ + break; + default: + return -EINVAL; } + state->audio_input = input; + return 0; +} + + +static int s2250_log_status(struct v4l2_subdev *sd) +{ + struct s2250 *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" : + state->std == V4L2_STD_PAL ? "PAL" : + state->std == V4L2_STD_SECAM ? "SECAM" : + "unknown"); + v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" : + state->input == 1 ? "S-video" : + "error"); + v4l2_info(sd, "Brightness: %d\n", state->brightness); + v4l2_info(sd, "Contrast: %d\n", state->contrast); + v4l2_info(sd, "Saturation: %d\n", state->saturation); + v4l2_info(sd, "Hue: %d\n", state->hue); return 0; + v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : + state->audio_input == 1 ? "Mic" : + state->audio_input == 2 ? "Mic Boost" : + "error"); return 0; } +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_subdev_core_ops s2250_core_ops = { + .log_status = s2250_log_status, + .g_ctrl = s2250_g_ctrl, + .s_ctrl = s2250_s_ctrl, + .queryctrl = s2250_queryctrl, + .s_std = s2250_s_std, +}; + +static const struct v4l2_subdev_audio_ops s2250_audio_ops = { + .s_routing = s2250_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops s2250_video_ops = { + .s_routing = s2250_s_video_routing, + .s_fmt = s2250_s_fmt, +}; + +static const struct v4l2_subdev_ops s2250_ops = { + .core = &s2250_core_ops, + .audio = &s2250_audio_ops, + .video = &s2250_video_ops, +}; + +/* --------------------------------------------------------------------------*/ + static int s2250_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct i2c_client *audio; struct i2c_adapter *adapter = client->adapter; - struct s2250 *dec; + struct s2250 *state; + struct v4l2_subdev *sd; u8 *data; struct go7007 *go = i2c_get_adapdata(adapter); struct go7007_usb *usb = go->hpi_context; @@ -561,30 +580,31 @@ static int s2250_probe(struct i2c_client *client, if (audio == NULL) return -ENOMEM; - dec = kmalloc(sizeof(struct s2250), GFP_KERNEL); - if (dec == NULL) { + state = kmalloc(sizeof(struct s2250), GFP_KERNEL); + if (state == NULL) { i2c_unregister_device(audio); return -ENOMEM; } - dec->std = V4L2_STD_NTSC; - dec->brightness = 50; - dec->contrast = 50; - dec->saturation = 50; - dec->hue = 0; - dec->audio = audio; - i2c_set_clientdata(client, dec); + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &s2250_ops); + + v4l2_info(sd, "initializing %s at address 0x%x on %s\n", + "Sensoray 2250/2251", client->addr, client->adapter->name); - printk(KERN_DEBUG - "s2250: initializing video decoder on %s\n", - adapter->name); + state->std = V4L2_STD_NTSC; + state->brightness = 50; + state->contrast = 50; + state->saturation = 50; + state->hue = 0; + state->audio = audio; /* initialize the audio */ if (write_regs(audio, aud_regs) < 0) { printk(KERN_ERR "s2250: error initializing audio\n"); i2c_unregister_device(audio); - kfree(dec); + kfree(state); return 0; } @@ -592,14 +612,14 @@ static int s2250_probe(struct i2c_client *client, printk(KERN_ERR "s2250: error initializing decoder\n"); i2c_unregister_device(audio); - kfree(dec); + kfree(state); return 0; } if (write_regs_fp(client, vid_regs_fp) < 0) { printk(KERN_ERR "s2250: error initializing decoder\n"); i2c_unregister_device(audio); - kfree(dec); + kfree(state); return 0; } /* set default channel */ @@ -609,7 +629,7 @@ static int s2250_probe(struct i2c_client *client, write_reg_fp(client, 0x140, 0x060); /* set default audio input */ - dec->audio_input = 0; + state->audio_input = 0; write_reg(client, 0x08, 0x02); /* Line In */ if (mutex_lock_interruptible(&usb->i2c_lock) == 0) { @@ -634,60 +654,28 @@ static int s2250_probe(struct i2c_client *client, mutex_unlock(&usb->i2c_lock); } - printk("s2250: initialized successfully\n"); + v4l2_info(sd, "initialized successfully\n"); return 0; } static int s2250_remove(struct i2c_client *client) { - struct s2250 *dec = i2c_get_clientdata(client); + struct v4l2_subdev *sd = i2c_get_clientdata(client); - i2c_set_clientdata(client, NULL); - i2c_unregister_device(dec->audio); - kfree(dec); + v4l2_device_unregister_subdev(sd); + kfree(to_state(sd)); return 0; } static struct i2c_device_id s2250_id[] = { - { "s2250_board", 0 }, + { "s2250", 0 }, { } }; +MODULE_DEVICE_TABLE(i2c, s2250_id); -static struct i2c_driver s2250_driver = { - .driver = { - .name = "Sensoray 2250 board driver", - }, - .probe = s2250_probe, - .remove = s2250_remove, - .command = s2250_command, - .id_table = s2250_id, +static struct v4l2_i2c_driver_data v4l2_i2c_data = { + .name = "s2250", + .probe = s2250_probe, + .remove = s2250_remove, + .id_table = s2250_id, }; - -static int __init s2250_init(void) -{ - int r; - - r = s2250loader_init(); - if (r < 0) - return r; - - r = i2c_add_driver(&s2250_driver); - if (r < 0) - s2250loader_cleanup(); - - return r; -} - -static void __exit s2250_cleanup(void) -{ - i2c_del_driver(&s2250_driver); - - s2250loader_cleanup(); -} - -module_init(s2250_init); -module_exit(s2250_cleanup); - -MODULE_AUTHOR(""); -MODULE_DESCRIPTION("Board driver for Sensoryray 2250"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/go7007/s2250-loader.c index d7bf829832747..c152ab9be2fb1 100644 --- a/drivers/staging/go7007/s2250-loader.c +++ b/drivers/staging/go7007/s2250-loader.c @@ -162,7 +162,7 @@ static struct usb_driver s2250loader_driver = { .id_table = s2250loader_ids, }; -int s2250loader_init(void) +static int __init s2250loader_init(void) { int r; unsigned i = 0; @@ -179,11 +179,15 @@ int s2250loader_init(void) printk(KERN_INFO "s2250loader_init: driver registered\n"); return 0; } -EXPORT_SYMBOL(s2250loader_init); +module_init(s2250loader_init); -void s2250loader_cleanup(void) +static void __exit s2250loader_cleanup(void) { printk(KERN_INFO "s2250loader_cleanup\n"); usb_deregister(&s2250loader_driver); } -EXPORT_SYMBOL(s2250loader_cleanup); +module_exit(s2250loader_cleanup); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index dfd4745a955f7..32b92298fd79e 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -361,6 +361,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ #define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */ #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ +#define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */ /* * F O R M A T E N U M E R A T I O N @@ -563,6 +564,7 @@ struct v4l2_framebuffer { #define V4L2_FBUF_CAP_LOCAL_ALPHA 0x0010 #define V4L2_FBUF_CAP_GLOBAL_ALPHA 0x0020 #define V4L2_FBUF_CAP_LOCAL_INV_ALPHA 0x0040 +#define V4L2_FBUF_CAP_SRC_CHROMAKEY 0x0080 /* Flags for the 'flags' field. */ #define V4L2_FBUF_FLAG_PRIMARY 0x0001 #define V4L2_FBUF_FLAG_OVERLAY 0x0002 @@ -570,6 +572,7 @@ struct v4l2_framebuffer { #define V4L2_FBUF_FLAG_LOCAL_ALPHA 0x0008 #define V4L2_FBUF_FLAG_GLOBAL_ALPHA 0x0010 #define V4L2_FBUF_FLAG_LOCAL_INV_ALPHA 0x0020 +#define V4L2_FBUF_FLAG_SRC_CHROMAKEY 0x0040 struct v4l2_clip { struct v4l2_rect c; @@ -912,8 +915,10 @@ enum v4l2_colorfx { #define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32) #define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33) +#define V4L2_CID_ROTATE (V4L2_CID_BASE+34) +#define V4L2_CID_BG_COLOR (V4L2_CID_BASE+35) /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+34) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+36) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) diff --git a/include/media/cx25840.h b/include/media/cx25840.h index 2c3fbaa33f744..0b0cb17767966 100644 --- a/include/media/cx25840.h +++ b/include/media/cx25840.h @@ -84,6 +84,7 @@ enum cx25840_video_input { CX25840_NONE0_CH3 = 0x80000080, CX25840_NONE1_CH3 = 0x800000c0, CX25840_SVIDEO_ON = 0x80000100, + CX25840_COMPONENT_ON = 0x80000200, }; enum cx25840_audio_input { diff --git a/include/media/davinci/vpfe_capture.h b/include/media/davinci/vpfe_capture.h index 71d8982e13ff4..d863e5e8426d7 100644 --- a/include/media/davinci/vpfe_capture.h +++ b/include/media/davinci/vpfe_capture.h @@ -83,6 +83,8 @@ struct vpfe_subdev_info { struct vpfe_config { /* Number of sub devices connected to vpfe */ int num_subdevs; + /* i2c bus adapter no */ + int i2c_adapter_id; /* information about each subdev */ struct vpfe_subdev_info *sub_devs; /* evm card info */ diff --git a/include/media/ir-common.h b/include/media/ir-common.h index 29f0e53cff94c..e41a99ee353e5 100644 --- a/include/media/ir-common.h +++ b/include/media/ir-common.h @@ -26,14 +26,16 @@ #include #include #include +#include + +extern int media_ir_debug; /* media_ir_debug level (0,1,2) */ +#define IR_dprintk(level, fmt, arg...) if (media_ir_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt , __func__, ## arg) #define IR_TYPE_RC5 1 #define IR_TYPE_PD 2 /* Pulse distance encoded IR */ #define IR_TYPE_OTHER 99 -#define IR_KEYTAB_TYPE u32 -#define IR_KEYTAB_SIZE 128 /* enougth for rc5, probably need more some day */ - struct ir_scancode { u16 scancode; u32 keycode; @@ -42,11 +44,9 @@ struct ir_scancode { struct ir_scancode_table { struct ir_scancode *scan; int size; + spinlock_t lock; }; -#define IR_KEYCODE(tab,code) (((unsigned)code < IR_KEYTAB_SIZE) \ - ? tab[code] : KEY_RESERVED) - #define RC5_START(x) (((x)>>12)&3) #define RC5_TOGGLE(x) (((x)>>11)&1) #define RC5_ADDR(x) (((x)>>6)&31) @@ -55,11 +55,11 @@ struct ir_scancode_table { struct ir_input_state { /* configuration */ int ir_type; - IR_KEYTAB_TYPE ir_codes[IR_KEYTAB_SIZE]; + + struct ir_scancode_table keytable; /* key info */ - u32 ir_raw; /* raw data */ - u32 ir_key; /* ir key code */ + u32 ir_key; /* ir scancode */ u32 keycode; /* linux key code */ int keypressed; /* current state */ }; @@ -102,20 +102,36 @@ struct card_ir { struct tasklet_struct tlet; }; -void ir_input_init(struct input_dev *dev, struct ir_input_state *ir, +/* Routines from ir-functions.c */ + +int ir_input_init(struct input_dev *dev, struct ir_input_state *ir, int ir_type, struct ir_scancode_table *ir_codes); void ir_input_nokey(struct input_dev *dev, struct ir_input_state *ir); void ir_input_keydown(struct input_dev *dev, struct ir_input_state *ir, - u32 ir_key, u32 ir_raw); + u32 ir_key); u32 ir_extract_bits(u32 data, u32 mask); int ir_dump_samples(u32 *samples, int count); int ir_decode_biphase(u32 *samples, int count, int low, int high); int ir_decode_pulsedistance(u32 *samples, int count, int low, int high); +u32 ir_rc5_decode(unsigned int code); void ir_rc5_timer_end(unsigned long data); void ir_rc5_timer_keyup(unsigned long data); -/* Keymaps to be used by other modules */ +/* Routines from ir-keytable.c */ + +u32 ir_g_keycode_from_table(struct input_dev *input_dev, + u32 scancode); + +int ir_set_keycode_table(struct input_dev *input_dev, + struct ir_scancode_table *rc_tab); + +int ir_roundup_tablesize(int n_elems); +int ir_copy_table(struct ir_scancode_table *destin, + const struct ir_scancode_table *origin); +void ir_input_free(struct input_dev *input_dev); + +/* scancode->keycode map tables from ir-keymaps.c */ extern struct ir_scancode_table ir_codes_empty_table; extern struct ir_scancode_table ir_codes_avermedia_table; @@ -150,6 +166,7 @@ extern struct ir_scancode_table ir_codes_rc5_tv_table; extern struct ir_scancode_table ir_codes_winfast_table; extern struct ir_scancode_table ir_codes_pinnacle_color_table; extern struct ir_scancode_table ir_codes_hauppauge_new_table; +extern struct ir_scancode_table ir_codes_rc5_hauppauge_new_table; extern struct ir_scancode_table ir_codes_npgtech_table; extern struct ir_scancode_table ir_codes_norwood_table; extern struct ir_scancode_table ir_codes_proteus_2309_table; @@ -172,6 +189,8 @@ extern struct ir_scancode_table ir_codes_ati_tv_wonder_hd_600_table; extern struct ir_scancode_table ir_codes_kworld_plus_tv_analog_table; extern struct ir_scancode_table ir_codes_kaiomy_table; extern struct ir_scancode_table ir_codes_dm1105_nec_table; +extern struct ir_scancode_table ir_codes_tevii_nec_table; +extern struct ir_scancode_table ir_codes_tbs_nec_table; extern struct ir_scancode_table ir_codes_evga_indtube_table; extern struct ir_scancode_table ir_codes_terratec_cinergy_xs_table; extern struct ir_scancode_table ir_codes_videomate_s350_table; diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index cf16689adba78..91942dbe64e3b 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -64,6 +64,7 @@ enum { V4L2_IDENT_OV9650 = 254, V4L2_IDENT_OV9655 = 255, V4L2_IDENT_SOI968 = 256, + V4L2_IDENT_OV9640 = 257, /* module saa7146: reserved range 300-309 */ V4L2_IDENT_SAA7146 = 300, @@ -72,6 +73,7 @@ enum { V4L2_IDENT_CX23418_843 = 403, /* Integrated A/V Decoder on the '418 */ V4L2_IDENT_CX23415 = 415, V4L2_IDENT_CX23416 = 416, + V4L2_IDENT_CX23417 = 417, V4L2_IDENT_CX23418 = 418, /* module au0828 */ @@ -129,6 +131,9 @@ enum { V4L2_IDENT_SAA6752HS = 6752, V4L2_IDENT_SAA6752HS_AC3 = 6753, + /* modules tef6862: just ident 6862 */ + V4L2_IDENT_TEF6862 = 6862, + /* module adv7170: just ident 7170 */ V4L2_IDENT_ADV7170 = 7170, @@ -165,12 +170,27 @@ enum { /* module mt9v011, just ident 8243 */ V4L2_IDENT_MT9V011 = 8243, + /* module cx23885 and cx25840 */ + V4L2_IDENT_CX23885 = 8850, + V4L2_IDENT_CX23885_AV = 8851, /* Integrated A/V decoder */ + V4L2_IDENT_CX23887 = 8870, + V4L2_IDENT_CX23887_AV = 8871, /* Integrated A/V decoder */ + V4L2_IDENT_CX23888 = 8880, + V4L2_IDENT_CX23888_AV = 8881, /* Integrated A/V decoder */ + V4L2_IDENT_CX23888_IR = 8882, /* Integrated infrared controller */ + /* module tw9910: just ident 9910 */ V4L2_IDENT_TW9910 = 9910, /* module sn9c20x: just ident 10000 */ V4L2_IDENT_SN9C20X = 10000, + /* module cx231xx and cx25840 */ + V4L2_IDENT_CX2310X_AV = 23099, /* Integrated A/V decoder; not in '100 */ + V4L2_IDENT_CX23100 = 23100, + V4L2_IDENT_CX23101 = 23101, + V4L2_IDENT_CX23102 = 23102, + /* module msp3400: reserved range 34000-34999 and 44000-44999 */ V4L2_IDENT_MSPX4XX = 34000, /* generic MSPX4XX identifier, only use internally (tveeprom.c). */ @@ -264,6 +284,9 @@ enum { /* module m52790: just ident 52790 */ V4L2_IDENT_M52790 = 52790, + + /* Sharp RJ54N1CB0C, 0xCB0C = 51980 */ + V4L2_IDENT_RJ54N1CB0C = 51980, }; #endif diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index d411345f244bf..00bf176084533 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -23,6 +23,16 @@ #include +/* generic v4l2_device notify callback notification values */ +#define V4L2_SUBDEV_IR_RX_NOTIFY _IOW('v', 0, u32) +#define V4L2_SUBDEV_IR_RX_FIFO_SERVICE_REQ 0x00000001 +#define V4L2_SUBDEV_IR_RX_END_OF_RX_DETECTED 0x00000002 +#define V4L2_SUBDEV_IR_RX_HW_FIFO_OVERRUN 0x00000004 +#define V4L2_SUBDEV_IR_RX_SW_FIFO_OVERRUN 0x00000008 + +#define V4L2_SUBDEV_IR_TX_NOTIFY _IOW('v', 1, u32) +#define V4L2_SUBDEV_IR_TX_FIFO_SERVICE_REQ 0x00000001 + struct v4l2_device; struct v4l2_subdev; struct tuner_setup; @@ -96,6 +106,9 @@ struct v4l2_decode_vbi_line { s_gpio: set GPIO pins. Very simple right now, might need to be extended with a direction argument if needed. + + s_power: puts subdevice in power saving mode (on == 0) or normal operation + mode (on == 1). */ struct v4l2_subdev_core_ops { int (*g_chip_ident)(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip); @@ -118,6 +131,7 @@ struct v4l2_subdev_core_ops { int (*g_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg); int (*s_register)(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg); #endif + int (*s_power)(struct v4l2_subdev *sd, int on); }; /* s_mode: switch the tuner to a specific tuner mode. Replacement of s_radio. @@ -127,8 +141,6 @@ struct v4l2_subdev_core_ops { s_type_addr: sets tuner type and its I2C addr. s_config: sets tda9887 specific stuff, like port1, port2 and qss - - s_standby: puts tuner on powersaving state, disabling it, except for i2c. */ struct v4l2_subdev_tuner_ops { int (*s_mode)(struct v4l2_subdev *sd, enum v4l2_tuner_type); @@ -141,7 +153,6 @@ struct v4l2_subdev_tuner_ops { int (*s_modulator)(struct v4l2_subdev *sd, struct v4l2_modulator *vm); int (*s_type_addr)(struct v4l2_subdev *sd, struct tuner_setup *type); int (*s_config)(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *config); - int (*s_standby)(struct v4l2_subdev *sd); }; /* s_clock_freq: set the frequency (in Hz) of the audio clock output. @@ -231,11 +242,95 @@ struct v4l2_subdev_video_ops { int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival); }; +/* + interrupt_service_routine: Called by the bridge chip's interrupt service + handler, when an IR interrupt status has be raised due to this subdev, + so that this subdev can handle the details. It may schedule work to be + performed later. It must not sleep. *Called from an IRQ context*. + + [rt]x_g_parameters: Get the current operating parameters and state of the + the IR receiver or transmitter. + + [rt]x_s_parameters: Set the current operating parameters and state of the + the IR receiver or transmitter. It is recommended to call + [rt]x_g_parameters first to fill out the current state, and only change + the fields that need to be changed. Upon return, the actual device + operating parameters and state will be returned. Note that hardware + limitations may prevent the actual settings from matching the requested + settings - e.g. an actual carrier setting of 35,904 Hz when 36,000 Hz + was requested. An exception is when the shutdown parameter is true. + The last used operational parameters will be returned, but the actual + state of the hardware be different to minimize power consumption and + processing when shutdown is true. + + rx_read: Reads received codes or pulse width data. + The semantics are similar to a non-blocking read() call. + + tx_write: Writes codes or pulse width data for transmission. + The semantics are similar to a non-blocking write() call. + */ + +enum v4l2_subdev_ir_mode { + V4L2_SUBDEV_IR_MODE_PULSE_WIDTH, /* space & mark widths in nanosecs */ +}; + +/* Data format of data read or written for V4L2_SUBDEV_IR_MODE_PULSE_WIDTH */ +#define V4L2_SUBDEV_IR_PULSE_MAX_WIDTH_NS 0x7fffffff +#define V4L2_SUBDEV_IR_PULSE_LEVEL_MASK 0x80000000 +#define V4L2_SUBDEV_IR_PULSE_RX_SEQ_END 0xffffffff + +struct v4l2_subdev_ir_parameters { + /* Either Rx or Tx */ + unsigned int bytes_per_data_element; /* of data in read or write call */ + enum v4l2_subdev_ir_mode mode; + + bool enable; + bool interrupt_enable; + bool shutdown; /* true: set hardware to low/no power, false: normal */ + + bool modulation; /* true: uses carrier, false: baseband */ + u32 max_pulse_width; /* ns, valid only for baseband signal */ + unsigned int carrier_freq; /* Hz, valid only for modulated signal*/ + unsigned int duty_cycle; /* percent, valid only for modulated signal*/ + bool invert; /* logically invert sense of mark/space */ + + /* Rx only */ + u32 noise_filter_min_width; /* ns, min time of a valid pulse */ + unsigned int carrier_range_lower; /* Hz, valid only for modulated sig */ + unsigned int carrier_range_upper; /* Hz, valid only for modulated sig */ + u32 resolution; /* ns */ +}; + +struct v4l2_subdev_ir_ops { + /* Common to receiver and transmitter */ + int (*interrupt_service_routine)(struct v4l2_subdev *sd, + u32 status, bool *handled); + + /* Receiver */ + int (*rx_read)(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num); + + int (*rx_g_parameters)(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *params); + int (*rx_s_parameters)(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *params); + + /* Transmitter */ + int (*tx_write)(struct v4l2_subdev *sd, u8 *buf, size_t count, + ssize_t *num); + + int (*tx_g_parameters)(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *params); + int (*tx_s_parameters)(struct v4l2_subdev *sd, + struct v4l2_subdev_ir_parameters *params); +}; + struct v4l2_subdev_ops { const struct v4l2_subdev_core_ops *core; const struct v4l2_subdev_tuner_ops *tuner; const struct v4l2_subdev_audio_ops *audio; const struct v4l2_subdev_video_ops *video; + const struct v4l2_subdev_ir_ops *ir; }; #define V4L2_SUBDEV_NAME_SIZE 32 @@ -290,7 +385,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd, Example: err = v4l2_subdev_call(sd, core, g_chip_ident, &chip); */ #define v4l2_subdev_call(sd, o, f, args...) \ - (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \ + (!(sd) ? -ENODEV : (((sd)->ops->o && (sd)->ops->o->f) ? \ (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD)) /* Send a notification to v4l2_device. */ diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h index 1c5946c447580..316fdccdcaa0d 100644 --- a/include/media/videobuf-core.h +++ b/include/media/videobuf-core.h @@ -166,7 +166,7 @@ struct videobuf_queue { enum v4l2_field field; enum v4l2_field last; /* for field=V4L2_FIELD_ALTERNATE */ struct videobuf_buffer *bufs[VIDEO_MAX_FRAME]; - struct videobuf_queue_ops *ops; + const struct videobuf_queue_ops *ops; struct videobuf_qtype_ops *int_ops; unsigned int streaming:1; @@ -195,7 +195,7 @@ void *videobuf_queue_to_vmalloc (struct videobuf_queue* q, struct videobuf_buffer *buf); void videobuf_queue_core_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h index 549386681aab8..ebaa9bc1ee8d5 100644 --- a/include/media/videobuf-dma-contig.h +++ b/include/media/videobuf-dma-contig.h @@ -17,7 +17,7 @@ #include void videobuf_queue_dma_contig_init(struct videobuf_queue *q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/include/media/videobuf-dma-sg.h b/include/media/videobuf-dma-sg.h index dda47f0082e99..53e72f7871757 100644 --- a/include/media/videobuf-dma-sg.h +++ b/include/media/videobuf-dma-sg.h @@ -103,7 +103,7 @@ struct videobuf_dmabuf *videobuf_to_dma (struct videobuf_buffer *buf); void *videobuf_sg_alloc(size_t size); void videobuf_queue_sg_init(struct videobuf_queue* q, - struct videobuf_queue_ops *ops, + const struct videobuf_queue_ops *ops, struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, diff --git a/include/media/videobuf-dvb.h b/include/media/videobuf-dvb.h index 6ba4f1271d237..07cf4b9d0a656 100644 --- a/include/media/videobuf-dvb.h +++ b/include/media/videobuf-dvb.h @@ -42,7 +42,9 @@ int videobuf_dvb_register_bus(struct videobuf_dvb_frontends *f, void *adapter_priv, struct device *device, short *adapter_nr, - int mfe_shared); + int mfe_shared, + int (*fe_ioctl_override)(struct dvb_frontend *, + unsigned int, void *, unsigned int)); void videobuf_dvb_unregister_bus(struct videobuf_dvb_frontends *f); diff --git a/include/media/videobuf-vmalloc.h b/include/media/videobuf-vmalloc.h index e87222c6a1250..4b419a257a7d9 100644 --- a/include/media/videobuf-vmalloc.h +++ b/include/media/videobuf-vmalloc.h @@ -30,8 +30,8 @@ struct videobuf_vmalloc_memory }; void videobuf_queue_vmalloc_init(struct videobuf_queue* q, - struct videobuf_queue_ops *ops, - void *dev, + const struct videobuf_queue_ops *ops, + struct device *dev, spinlock_t *irqlock, enum v4l2_buf_type type, enum v4l2_field field,