diff --git a/Documentation/DocBook/media/Makefile b/Documentation/DocBook/media/Makefile index 6628b4b9cac4..362520992ced 100644 --- a/Documentation/DocBook/media/Makefile +++ b/Documentation/DocBook/media/Makefile @@ -70,6 +70,8 @@ IOCTLS = \ VIDIOC_SUBDEV_ENUM_MBUS_CODE \ VIDIOC_SUBDEV_ENUM_FRAME_SIZE \ VIDIOC_SUBDEV_ENUM_FRAME_INTERVAL \ + VIDIOC_SUBDEV_G_SELECTION \ + VIDIOC_SUBDEV_S_SELECTION \ TYPES = \ $(shell perl -ne 'print "$$1 " if /^typedef\s+[^\s]+\s+([^\s]+)\;/' $(srctree)/include/linux/videodev2.h) \ @@ -193,7 +195,7 @@ DVB_DOCUMENTED = \ # install_media_images = \ - $(Q)cp $(OBJIMGFILES) $(MEDIA_OBJ_DIR)/media_api + $(Q)cp $(OBJIMGFILES) $(MEDIA_SRC_DIR)/v4l/*.svg $(MEDIA_OBJ_DIR)/media_api $(MEDIA_OBJ_DIR)/%: $(MEDIA_SRC_DIR)/%.b64 $(Q)base64 -d $< >$@ diff --git a/Documentation/DocBook/media/dvb/dvbproperty.xml b/Documentation/DocBook/media/dvb/dvbproperty.xml index c7a4ca517859..e633c097a8d1 100644 --- a/Documentation/DocBook/media/dvb/dvbproperty.xml +++ b/Documentation/DocBook/media/dvb/dvbproperty.xml @@ -531,6 +531,139 @@ typedef enum fe_delivery_system { here are referring to what can be found in the TMCC-structure - independent of the mode. +
+ <constant>DTV_ATSCMH_FIC_VER</constant> + Version number of the FIC (Fast Information Channel) signaling data. + FIC is used for relaying information to allow rapid service acquisition by the receiver. + Possible values: 0, 1, 2, 3, ..., 30, 31 +
+
+ <constant>DTV_ATSCMH_PARADE_ID</constant> + Parade identification number + A parade is a collection of up to eight MH groups, conveying one or two ensembles. + Possible values: 0, 1, 2, 3, ..., 126, 127 +
+
+ <constant>DTV_ATSCMH_NOG</constant> + Number of MH groups per MH subframe for a designated parade. + Possible values: 1, 2, 3, 4, 5, 6, 7, 8 +
+
+ <constant>DTV_ATSCMH_TNOG</constant> + Total number of MH groups including all MH groups belonging to all MH parades in one MH subframe. + Possible values: 0, 1, 2, 3, ..., 30, 31 +
+
+ <constant>DTV_ATSCMH_SGN</constant> + Start group number. + Possible values: 0, 1, 2, 3, ..., 14, 15 +
+
+ <constant>DTV_ATSCMH_PRC</constant> + Parade repetition cycle. + Possible values: 1, 2, 3, 4, 5, 6, 7, 8 +
+
+ <constant>DTV_ATSCMH_RS_FRAME_MODE</constant> + RS frame mode. + Possible values are: + +typedef enum atscmh_rs_frame_mode { + ATSCMH_RSFRAME_PRI_ONLY = 0, + ATSCMH_RSFRAME_PRI_SEC = 1, +} atscmh_rs_frame_mode_t; + +
+
+ <constant>DTV_ATSCMH_RS_FRAME_ENSEMBLE</constant> + RS frame ensemble. + Possible values are: + +typedef enum atscmh_rs_frame_ensemble { + ATSCMH_RSFRAME_ENS_PRI = 0, + ATSCMH_RSFRAME_ENS_SEC = 1, +} atscmh_rs_frame_ensemble_t; + +
+
+ <constant>DTV_ATSCMH_RS_CODE_MODE_PRI</constant> + RS code mode (primary). + Possible values are: + +typedef enum atscmh_rs_code_mode { + ATSCMH_RSCODE_211_187 = 0, + ATSCMH_RSCODE_223_187 = 1, + ATSCMH_RSCODE_235_187 = 2, +} atscmh_rs_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_RS_CODE_MODE_SEC</constant> + RS code mode (secondary). + Possible values are: + +typedef enum atscmh_rs_code_mode { + ATSCMH_RSCODE_211_187 = 0, + ATSCMH_RSCODE_223_187 = 1, + ATSCMH_RSCODE_235_187 = 2, +} atscmh_rs_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_BLOCK_MODE</constant> + Series Concatenated Convolutional Code Block Mode. + Possible values are: + +typedef enum atscmh_sccc_block_mode { + ATSCMH_SCCC_BLK_SEP = 0, + ATSCMH_SCCC_BLK_COMB = 1, +} atscmh_sccc_block_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_CODE_MODE_A</constant> + Series Concatenated Convolutional Code Rate. + Possible values are: + +typedef enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, +} atscmh_sccc_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_CODE_MODE_B</constant> + Series Concatenated Convolutional Code Rate. + Possible values are: + +typedef enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, +} atscmh_sccc_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_CODE_MODE_C</constant> + Series Concatenated Convolutional Code Rate. + Possible values are: + +typedef enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, +} atscmh_sccc_code_mode_t; + +
+
+ <constant>DTV_ATSCMH_SCCC_CODE_MODE_D</constant> + Series Concatenated Convolutional Code Rate. + Possible values are: + +typedef enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, +} atscmh_sccc_code_mode_t; + +
<constant>DTV_API_VERSION</constant> @@ -774,6 +907,33 @@ typedef enum fe_hierarchy { DTV_BANDWIDTH_HZ
+
+ ATSC-MH delivery system + The following parameters are valid for ATSC-MH: + + DTV_API_VERSION + DTV_DELIVERY_SYSTEM + DTV_TUNE + DTV_CLEAR + DTV_FREQUENCY + DTV_BANDWIDTH_HZ + DTV_ATSCMH_FIC_VER + DTV_ATSCMH_PARADE_ID + DTV_ATSCMH_NOG + DTV_ATSCMH_TNOG + DTV_ATSCMH_SGN + DTV_ATSCMH_PRC + DTV_ATSCMH_RS_FRAME_MODE + DTV_ATSCMH_RS_FRAME_ENSEMBLE + DTV_ATSCMH_CODE_MODE_PRI + DTV_ATSCMH_CODE_MODE_SEC + DTV_ATSCMH_SCCC_BLOCK_MODE + DTV_ATSCMH_SCCC_CODE_MODE_A + DTV_ATSCMH_SCCC_CODE_MODE_B + DTV_ATSCMH_SCCC_CODE_MODE_C + DTV_ATSCMH_SCCC_CODE_MODE_D + +
Properties used on cable delivery systems diff --git a/Documentation/DocBook/media/v4l/biblio.xml b/Documentation/DocBook/media/v4l/biblio.xml index 7dc65c592a87..7c49facecd25 100644 --- a/Documentation/DocBook/media/v4l/biblio.xml +++ b/Documentation/DocBook/media/v4l/biblio.xml @@ -197,4 +197,33 @@ in the frequency range from 87,5 to 108,0 MHz NTSC-4: United States RBDS Standard + + ISO 12232:2006 + + International Organization for Standardization +(http://www.iso.org) + + Photography — Digital still cameras — Determination + of exposure index, ISO speed ratings, standard output sensitivity, and + recommended exposure index + + + + CEA-861-E + + Consumer Electronics Association +(http://www.ce.org) + + A DTV Profile for Uncompressed High Speed Digital Interfaces + + + + VESA DMT + + Video Electronics Standards Association +(http://www.vesa.org) + + VESA and Industry Standards and Guidelines for Computer Display Monitor Timing (DMT) + + diff --git a/Documentation/DocBook/media/v4l/common.xml b/Documentation/DocBook/media/v4l/common.xml index c79278acfb0e..4101aeb56540 100644 --- a/Documentation/DocBook/media/v4l/common.xml +++ b/Documentation/DocBook/media/v4l/common.xml @@ -724,41 +724,49 @@ if (-1 == ioctl (fd, &VIDIOC-S-STD;, &std_id)) { } +
Digital Video (DV) Timings - The video standards discussed so far has been dealing with Analog TV and the + The video standards discussed so far have been dealing with Analog TV and the corresponding video timings. Today there are many more different hardware interfaces such as High Definition TV interfaces (HDMI), VGA, DVI connectors etc., that carry video signals and there is a need to extend the API to select the video timings for these interfaces. Since it is not possible to extend the &v4l2-std-id; due to -the limited bits available, a new set of IOCTLs is added to set/get video timings at +the limited bits available, a new set of IOCTLs was added to set/get video timings at the input and output: - DV Presets: Digital Video (DV) presets. These are IDs representing a + DV Timings: This will allow applications to define detailed +video timings for the interface. This includes parameters such as width, height, +polarities, frontporch, backporch etc. The linux/v4l2-dv-timings.h +header can be used to get the timings of the formats in the and + standards. + + + + DV Presets: Digital Video (DV) presets (deprecated). + These are IDs representing a video timing at the input/output. Presets are pre-defined timings implemented by the hardware according to video standards. A __u32 data type is used to represent a preset unlike the bit mask that is used in &v4l2-std-id; allowing future extensions -to support as many different presets as needed. - - - Custom DV Timings: This will allow applications to define more detailed -custom video timings for the interface. This includes parameters such as width, height, -polarities, frontporch, backporch etc. - +to support as many different presets as needed. This API is deprecated in favor of the DV Timings +API. + To enumerate and query the attributes of the DV timings supported by a device, + applications use the &VIDIOC-ENUM-DV-TIMINGS; and &VIDIOC-DV-TIMINGS-CAP; ioctls. + To set DV timings for the device, applications use the +&VIDIOC-S-DV-TIMINGS; ioctl and to get current DV timings they use the +&VIDIOC-G-DV-TIMINGS; ioctl. To detect the DV timings as seen by the video receiver applications +use the &VIDIOC-QUERY-DV-TIMINGS; ioctl. To enumerate and query the attributes of DV presets supported by a device, applications use the &VIDIOC-ENUM-DV-PRESETS; ioctl. To get the current DV preset, applications use the &VIDIOC-G-DV-PRESET; ioctl and to set a preset they use the -&VIDIOC-S-DV-PRESET; ioctl. - To set custom DV timings for the device, applications use the -&VIDIOC-S-DV-TIMINGS; ioctl and to get current custom DV timings they use the -&VIDIOC-G-DV-TIMINGS; ioctl. +&VIDIOC-S-DV-PRESET; ioctl. To detect the preset as seen by the video receiver applications +use the &VIDIOC-QUERY-DV-PRESET; ioctl. Applications can make use of the and flags to decide what ioctls are available to set the video timings for the device. -
&sub-controls; diff --git a/Documentation/DocBook/media/v4l/compat.xml b/Documentation/DocBook/media/v4l/compat.xml index bce97c50391b..ea42ef824948 100644 --- a/Documentation/DocBook/media/v4l/compat.xml +++ b/Documentation/DocBook/media/v4l/compat.xml @@ -2407,6 +2407,54 @@ details. Added JPEG compression control class. + + Extended the DV Timings API: + &VIDIOC-ENUM-DV-TIMINGS;, &VIDIOC-QUERY-DV-TIMINGS; and + &VIDIOC-DV-TIMINGS-CAP;. + + + + +
+ V4L2 in Linux 3.5 + + + Added integer menus, the new type will be + V4L2_CTRL_TYPE_INTEGER_MENU. + + + Added selection API for V4L2 subdev interface: + &VIDIOC-SUBDEV-G-SELECTION; and + &VIDIOC-SUBDEV-S-SELECTION;. + + + Added V4L2_COLORFX_ANTIQUE, + V4L2_COLORFX_ART_FREEZE, + V4L2_COLORFX_AQUA, + V4L2_COLORFX_SILHOUETTE, + V4L2_COLORFX_SOLARIZATION, + V4L2_COLORFX_VIVID and + V4L2_COLORFX_ARBITRARY_CBCR menu items + to the V4L2_CID_COLORFX control. + + + Added V4L2_CID_COLORFX_CBCR control. + + + Added camera controls V4L2_CID_AUTO_EXPOSURE_BIAS, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + V4L2_CID_IMAGE_STABILIZATION, + V4L2_CID_ISO_SENSITIVITY, + V4L2_CID_ISO_SENSITIVITY_AUTO, + V4L2_CID_EXPOSURE_METERING, + V4L2_CID_SCENE_MODE, + V4L2_CID_3A_LOCK, + V4L2_CID_AUTO_FOCUS_START, + V4L2_CID_AUTO_FOCUS_STOP, + V4L2_CID_AUTO_FOCUS_STATUS and + V4L2_CID_AUTO_FOCUS_RANGE. + +
@@ -2505,6 +2553,10 @@ and may change in the future. &VIDIOC-ENCODER-CMD; and &VIDIOC-TRY-ENCODER-CMD; +ioctls. + + + &VIDIOC-DECODER-CMD; and &VIDIOC-TRY-DECODER-CMD; ioctls. @@ -2514,6 +2566,10 @@ ioctls. &VIDIOC-DBG-G-CHIP-IDENT; ioctl. + + &VIDIOC-ENUM-DV-TIMINGS;, &VIDIOC-QUERY-DV-TIMINGS; and + &VIDIOC-DV-TIMINGS-CAP; ioctls. + Flash API. @@ -2523,6 +2579,14 @@ ioctls. Selection API. + + Sub-device selection API: &VIDIOC-SUBDEV-G-SELECTION; + and &VIDIOC-SUBDEV-S-SELECTION; ioctls. + + + + V4L2_CID_AUTO_FOCUS_AREA control. + @@ -2538,6 +2602,17 @@ interfaces and should not be implemented in new drivers. VIDIOC_S_MPEGCOMP ioctls. Use Extended Controls, . + + &VIDIOC-G-DV-PRESET;, &VIDIOC-S-DV-PRESET;, &VIDIOC-ENUM-DV-PRESETS; and + &VIDIOC-QUERY-DV-PRESET; ioctls. Use the DV Timings API (). + + + VIDIOC_SUBDEV_G_CROP and + VIDIOC_SUBDEV_S_CROP ioctls. Use + VIDIOC_SUBDEV_G_SELECTION and + VIDIOC_SUBDEV_S_SELECTION, . + diff --git a/Documentation/DocBook/media/v4l/controls.xml b/Documentation/DocBook/media/v4l/controls.xml index dd03cf4a6539..676bc46f9c52 100644 --- a/Documentation/DocBook/media/v4l/controls.xml +++ b/Documentation/DocBook/media/v4l/controls.xml @@ -285,18 +285,92 @@ minimum value disables backlight compensation. V4L2_CID_COLORFX enum - Selects a color effect. Possible values for -enum v4l2_colorfx are: -V4L2_COLORFX_NONE (0), -V4L2_COLORFX_BW (1), -V4L2_COLORFX_SEPIA (2), -V4L2_COLORFX_NEGATIVE (3), -V4L2_COLORFX_EMBOSS (4), -V4L2_COLORFX_SKETCH (5), -V4L2_COLORFX_SKY_BLUE (6), -V4L2_COLORFX_GRASS_GREEN (7), -V4L2_COLORFX_SKIN_WHITEN (8) and -V4L2_COLORFX_VIVID (9). + Selects a color effect. The following values are defined: + + + + + + + + V4L2_COLORFX_NONE  + Color effect is disabled. + + + V4L2_COLORFX_ANTIQUE  + An aging (old photo) effect. + + + V4L2_COLORFX_ART_FREEZE  + Frost color effect. + + + V4L2_COLORFX_AQUA  + Water color, cool tone. + + + V4L2_COLORFX_BW  + Black and white. + + + V4L2_COLORFX_EMBOSS  + Emboss, the highlights and shadows replace light/dark boundaries + and low contrast areas are set to a gray background. + + + V4L2_COLORFX_GRASS_GREEN  + Grass green. + + + V4L2_COLORFX_NEGATIVE  + Negative. + + + V4L2_COLORFX_SEPIA  + Sepia tone. + + + V4L2_COLORFX_SKETCH  + Sketch. + + + V4L2_COLORFX_SKIN_WHITEN  + Skin whiten. + + + V4L2_COLORFX_SKY_BLUE  + Sky blue. + + + V4L2_COLORFX_SOLARIZATION  + Solarization, the image is partially reversed in tone, + only color values above or below a certain threshold are inverted. + + + + V4L2_COLORFX_SILHOUETTE  + Silhouette (outline). + + + V4L2_COLORFX_VIVID  + Vivid colors. + + + V4L2_COLORFX_SET_CBCR  + The Cb and Cr chroma components are replaced by fixed + coefficients determined by V4L2_CID_COLORFX_CBCR + control. + + + + + + V4L2_CID_COLORFX_CBCR + integer + Determines the Cb and Cr coefficients for V4L2_COLORFX_SET_CBCR + color effect. Bits [7:0] of the supplied 32 bit value are interpreted as + Cr component, bits [15:8] as Cb component and bits [31:16] must be zero. + V4L2_CID_ROTATE @@ -2774,6 +2848,51 @@ remain constant. + + V4L2_CID_EXPOSURE_BIAS  + integer menu + Determines the automatic +exposure compensation, it is effective only when V4L2_CID_EXPOSURE_AUTO +control is set to AUTO, SHUTTER_PRIORITY +or APERTURE_PRIORITY. +It is expressed in terms of EV, drivers should interpret the values as 0.001 EV +units, where the value 1000 stands for +1 EV. +Increasing the exposure compensation value is equivalent to decreasing +the exposure value (EV) and will increase the amount of light at the image +sensor. The camera performs the exposure compensation by adjusting absolute +exposure time and/or aperture. + + + + + V4L2_CID_EXPOSURE_METERING  + enum v4l2_exposure_metering + Determines how the camera measures +the amount of light available for the frame exposure. Possible values are: + + + + + + V4L2_EXPOSURE_METERING_AVERAGE  + Use the light information coming from the entire frame +and average giving no weighting to any particular portion of the metered area. + + + + V4L2_EXPOSURE_METERING_CENTER_WEIGHTED  + Average the light information coming from the entire frame +giving priority to the center of the metered area. + + + V4L2_EXPOSURE_METERING_SPOT  + Measure only very small area at the center of the frame. + + + + + + V4L2_CID_PAN_RELATIVE  integer @@ -2857,12 +2976,106 @@ negative values towards infinity. This is a write-only control. V4L2_CID_FOCUS_AUTO  boolean - Enables automatic focus -adjustments. The effect of manual focus adjustments while this feature + Enables continuous automatic +focus adjustments. The effect of manual focus adjustments while this feature is enabled is undefined, drivers should ignore such requests. + + V4L2_CID_AUTO_FOCUS_START  + button + Starts single auto focus process. +The effect of setting this control when V4L2_CID_FOCUS_AUTO +is set to TRUE (1) is undefined, drivers should ignore +such requests. + + + + + V4L2_CID_AUTO_FOCUS_STOP  + button + Aborts automatic focusing +started with V4L2_CID_AUTO_FOCUS_START control. It is +effective only when the continuous autofocus is disabled, that is when +V4L2_CID_FOCUS_AUTO control is set to FALSE + (0). + + + + + + V4L2_CID_AUTO_FOCUS_STATUS  + bitmask + + The automatic focus status. This is a read-only + control. + + + + + + V4L2_AUTO_FOCUS_STATUS_IDLE  + Automatic focus is not active. + + + V4L2_AUTO_FOCUS_STATUS_BUSY  + Automatic focusing is in progress. + + + V4L2_AUTO_FOCUS_STATUS_REACHED  + Focus has been reached. + + + V4L2_AUTO_FOCUS_STATUS_FAILED  + Automatic focus has failed, the driver will not + transition from this state until another action is + performed by an application. + + + + + +Setting V4L2_LOCK_FOCUS lock bit of the V4L2_CID_3A_LOCK + control may stop updates of the V4L2_CID_AUTO_FOCUS_STATUS +control value. + + + + + + V4L2_CID_AUTO_FOCUS_RANGE  + enum v4l2_auto_focus_range + + Determines auto focus distance range +for which lens may be adjusted. + + + + + + V4L2_AUTO_FOCUS_RANGE_AUTO  + The camera automatically selects the focus range. + + + V4L2_AUTO_FOCUS_RANGE_NORMAL  + Normal distance range, limited for best automatic focus +performance. + + + V4L2_AUTO_FOCUS_RANGE_MACRO  + Macro (close-up) auto focus. The camera will +use its minimum possible distance for auto focus. + + + V4L2_AUTO_FOCUS_RANGE_INFINITY  + The lens is set to focus on an object at infinite distance. + + + + + + V4L2_CID_ZOOM_ABSOLUTE  integer @@ -2932,6 +3145,295 @@ camera sensor on or off, or specify its strength. Such band-stop filters can be used, for example, to filter out the fluorescent light component. + + + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE  + enum v4l2_auto_n_preset_white_balance + Sets white balance to automatic, +manual or a preset. The presets determine color temperature of the light as +a hint to the camera for white balance adjustments resulting in most accurate +color representation. The following white balance presets are listed in order +of increasing color temperature. + + + + + + V4L2_WHITE_BALANCE_MANUAL  + Manual white balance. + + + V4L2_WHITE_BALANCE_AUTO  + Automatic white balance adjustments. + + + V4L2_WHITE_BALANCE_INCANDESCENT  + White balance setting for incandescent (tungsten) lighting. +It generally cools down the colors and corresponds approximately to 2500...3500 K +color temperature range. + + + V4L2_WHITE_BALANCE_FLUORESCENT  + White balance preset for fluorescent lighting. +It corresponds approximately to 4000...5000 K color temperature. + + + V4L2_WHITE_BALANCE_FLUORESCENT_H  + With this setting the camera will compensate for +fluorescent H lighting. + + + V4L2_WHITE_BALANCE_HORIZON  + White balance setting for horizon daylight. +It corresponds approximately to 5000 K color temperature. + + + V4L2_WHITE_BALANCE_DAYLIGHT  + White balance preset for daylight (with clear sky). +It corresponds approximately to 5000...6500 K color temperature. + + + V4L2_WHITE_BALANCE_FLASH  + With this setting the camera will compensate for the flash +light. It slightly warms up the colors and corresponds roughly to 5000...5500 K +color temperature. + + + V4L2_WHITE_BALANCE_CLOUDY  + White balance preset for moderately overcast sky. +This option corresponds approximately to 6500...8000 K color temperature +range. + + + V4L2_WHITE_BALANCE_SHADE  + White balance preset for shade or heavily overcast +sky. It corresponds approximately to 9000...10000 K color temperature. + + + + + + + + + V4L2_CID_WIDE_DYNAMIC_RANGE + boolean + + + Enables or disables the camera's wide dynamic +range feature. This feature allows to obtain clear images in situations where +intensity of the illumination varies significantly throughout the scene, i.e. +there are simultaneously very dark and very bright areas. It is most commonly +realized in cameras by combining two subsequent frames with different exposure +times. This control may be changed to a menu +control in the future, if more options are required. + + + + + V4L2_CID_IMAGE_STABILIZATION + boolean + + + Enables or disables image stabilization. + + + + + + V4L2_CID_ISO_SENSITIVITY  + integer menu + Determines ISO equivalent of an +image sensor indicating the sensor's sensitivity to light. The numbers are +expressed in arithmetic scale, as per standard, +where doubling the sensor sensitivity is represented by doubling the numerical +ISO value. Applications should interpret the values as standard ISO values +multiplied by 1000, e.g. control value 800 stands for ISO 0.8. Drivers will +usually support only a subset of standard ISO values. The effect of setting +this control while the V4L2_CID_ISO_SENSITIVITY_AUTO +control is set to a value other than V4L2_CID_ISO_SENSITIVITY_MANUAL + is undefined, drivers should ignore such requests. + + + + + V4L2_CID_ISO_SENSITIVITY_AUTO  + enum v4l2_iso_sensitivity_type + Enables or disables automatic ISO +sensitivity adjustments. + + + + + + V4L2_CID_ISO_SENSITIVITY_MANUAL  + Manual ISO sensitivity. + + + V4L2_CID_ISO_SENSITIVITY_AUTO  + Automatic ISO sensitivity adjustments. + + + + + + + + V4L2_CID_SCENE_MODE  + enum v4l2_scene_mode + This control allows to select +scene programs as the camera automatic modes optimized for common shooting +scenes. Within these modes the camera determines best exposure, aperture, +focusing, light metering, white balance and equivalent sensitivity. The +controls of those parameters are influenced by the scene mode control. +An exact behavior in each mode is subject to the camera specification. + +When the scene mode feature is not used, this control should be set to +V4L2_SCENE_MODE_NONE to make sure the other possibly +related controls are accessible. The following scene programs are defined: + + + + + + + + V4L2_SCENE_MODE_NONE  + The scene mode feature is disabled. + + + V4L2_SCENE_MODE_BACKLIGHT  + Backlight. Compensates for dark shadows when light is + coming from behind a subject, also by automatically turning + on the flash. + + + V4L2_SCENE_MODE_BEACH_SNOW  + Beach and snow. This mode compensates for all-white or +bright scenes, which tend to look gray and low contrast, when camera's automatic +exposure is based on an average scene brightness. To compensate, this mode +automatically slightly overexposes the frames. The white balance may also be +adjusted to compensate for the fact that reflected snow looks bluish rather +than white. + + + V4L2_SCENE_MODE_CANDLELIGHT  + Candle light. The camera generally raises the ISO +sensitivity and lowers the shutter speed. This mode compensates for relatively +close subject in the scene. The flash is disabled in order to preserve the +ambiance of the light. + + + V4L2_SCENE_MODE_DAWN_DUSK  + Dawn and dusk. Preserves the colors seen in low +natural light before dusk and after down. The camera may turn off the flash, +and automatically focus at infinity. It will usually boost saturation and +lower the shutter speed. + + + V4L2_SCENE_MODE_FALL_COLORS  + Fall colors. Increases saturation and adjusts white +balance for color enhancement. Pictures of autumn leaves get saturated reds +and yellows. + + + V4L2_SCENE_MODE_FIREWORKS  + Fireworks. Long exposure times are used to capture +the expanding burst of light from a firework. The camera may invoke image +stabilization. + + + V4L2_SCENE_MODE_LANDSCAPE  + Landscape. The camera may choose a small aperture to +provide deep depth of field and long exposure duration to help capture detail +in dim light conditions. The focus is fixed at infinity. Suitable for distant +and wide scenery. + + + V4L2_SCENE_MODE_NIGHT  + Night, also known as Night Landscape. Designed for low +light conditions, it preserves detail in the dark areas without blowing out bright +objects. The camera generally sets itself to a medium-to-high ISO sensitivity, +with a relatively long exposure time, and turns flash off. As such, there will be +increased image noise and the possibility of blurred image. + + + V4L2_SCENE_MODE_PARTY_INDOOR  + Party and indoor. Designed to capture indoor scenes +that are lit by indoor background lighting as well as the flash. The camera +usually increases ISO sensitivity, and adjusts exposure for the low light +conditions. + + + V4L2_SCENE_MODE_PORTRAIT  + Portrait. The camera adjusts the aperture so that the +depth of field is reduced, which helps to isolate the subject against a smooth +background. Most cameras recognize the presence of faces in the scene and focus +on them. The color hue is adjusted to enhance skin tones. The intensity of the +flash is often reduced. + + + V4L2_SCENE_MODE_SPORTS  + Sports. Significantly increases ISO and uses a fast +shutter speed to freeze motion of rapidly-moving subjects. Increased image +noise may be seen in this mode. + + + V4L2_SCENE_MODE_SUNSET  + Sunset. Preserves deep hues seen in sunsets and +sunrises. It bumps up the saturation. + + + V4L2_SCENE_MODE_TEXT  + Text. It applies extra contrast and sharpness, it is +typically a black-and-white mode optimized for readability. Automatic focus +may be switched to close-up mode and this setting may also involve some +lens-distortion correction. + + + + + + + + V4L2_CID_3A_LOCK + bitmask + + + This control locks or unlocks the automatic +focus, exposure and white balance. The automatic adjustments can be paused +independently by setting the corresponding lock bit to 1. The camera then retains +the settings until the lock bit is cleared. The following lock bits are defined: + + + + + + + V4L2_LOCK_EXPOSURE + Automatic exposure adjustments lock. + + + V4L2_LOCK_WHITE_BALANCE + Automatic white balance adjustments lock. + + + V4L2_LOCK_FOCUS + Automatic focus lock. + + + + + +When a given algorithm is not enabled, drivers should ignore requests +to lock it and should return no error. An example might be an application +setting bit V4L2_LOCK_WHITE_BALANCE when the +V4L2_CID_AUTO_WHITE_BALANCE control is set to +FALSE. The value of this control may be changed +by exposure, white balance or focus controls. + + + @@ -3476,7 +3978,7 @@ interface and may change in the future. V4L2_CID_JPEG_CHROMA_SUBSAMPLING menu - + The chroma subsampling factors describe how each component of an input image is sampled, in respect to maximum sample rate in each spatial dimension. See , @@ -3486,7 +3988,7 @@ interface and may change in the future. from RGB to Y'CbCr color space. - + @@ -3538,12 +4040,12 @@ interface and may change in the future. - V4L2_CID_JPEG_COMPRESION_QUALITY + V4L2_CID_JPEG_COMPRESSION_QUALITY integer - V4L2_CID_JPEG_COMPRESION_QUALITY control + V4L2_CID_JPEG_COMPRESSION_QUALITY control determines trade-off between image quality and size. It provides simpler method for applications to control image quality, without a need for direct reconfiguration of luminance and chrominance @@ -3551,7 +4053,7 @@ interface and may change in the future. In cases where a driver uses quantization tables configured directly by an application, using interfaces defined elsewhere, - V4L2_CID_JPEG_COMPRESION_QUALITY control should be set + V4L2_CID_JPEG_COMPRESSION_QUALITY control should be set by driver to 0. The value range of this control is driver-specific. Only @@ -3599,4 +4101,172 @@ interface and may change in the future. to , , . + +
+ Image Source Control Reference + + + Experimental + + This is an experimental interface and may + change in the future. + + + + The Image Source control class is intended for low-level + control of image source devices such as image sensors. The + devices feature an analogue to digital converter and a bus + transmitter to transmit the image data out of the device. + + + + Image Source Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + V4L2_CID_IMAGE_SOURCE_CLASS + class + + + The IMAGE_SOURCE class descriptor. + + + V4L2_CID_VBLANK + integer + + + Vertical blanking. The idle period + after every frame during which no image data is produced. + The unit of vertical blanking is a line. Every line has + length of the image width plus horizontal blanking at the + pixel rate defined by + V4L2_CID_PIXEL_RATE control in the + same sub-device. + + + V4L2_CID_HBLANK + integer + + + Horizontal blanking. The idle + period after every line of image data during which no + image data is produced. The unit of horizontal blanking is + pixels. + + + V4L2_CID_ANALOGUE_GAIN + integer + + + Analogue gain is gain affecting + all colour components in the pixel matrix. The gain + operation is performed in the analogue domain before A/D + conversion. + + + + + +
+ +
+ +
+ Image Process Control Reference + + + Experimental + + This is an experimental interface and may + change in the future. + + + + The Image Source control class is intended for low-level control of + image processing functions. Unlike + V4L2_CID_IMAGE_SOURCE_CLASS, the controls in + this class affect processing the image, and do not control capturing + of it. + + + + Image Source Control IDs + + + + + + + + + + + ID + Type + Description + + + + + + V4L2_CID_IMAGE_PROC_CLASS + class + + + The IMAGE_PROC class descriptor. + + + V4L2_CID_LINK_FREQ + integer menu + + + Data bus frequency. Together with the + media bus pixel code, bus type (clock cycles per sample), the + data bus frequency defines the pixel rate + (V4L2_CID_PIXEL_RATE) in the + pixel array (or possibly elsewhere, if the device is not an + image sensor). The frame rate can be calculated from the pixel + clock, image width and height and horizontal and vertical + blanking. While the pixel rate control may be defined elsewhere + than in the subdev containing the pixel array, the frame rate + cannot be obtained from that information. This is because only + on the pixel array it can be assumed that the vertical and + horizontal blanking information is exact: no other blanking is + allowed in the pixel array. The selection of frame rate is + performed by selecting the desired horizontal and vertical + blanking. The unit of this control is Hz. + + + V4L2_CID_PIXEL_RATE + 64-bit integer + + + Pixel rate in the source pads of + the subdev. This control is read-only and its unit is + pixels / second. + + + + + +
+ +
diff --git a/Documentation/DocBook/media/v4l/dev-subdev.xml b/Documentation/DocBook/media/v4l/dev-subdev.xml index 0916a7343a16..4afcbbec5eda 100644 --- a/Documentation/DocBook/media/v4l/dev-subdev.xml +++ b/Documentation/DocBook/media/v4l/dev-subdev.xml @@ -76,11 +76,12 @@ format means the combination of media bus data format, frame width and frame height. - Image formats are typically negotiated on video capture and output - devices using the cropping and scaling ioctls. - The driver is responsible for configuring every block in the video pipeline - according to the requested format at the pipeline input and/or - output. + Image formats are typically negotiated on video capture and + output devices using the format and selection ioctls. The + driver is responsible for configuring every block in the video + pipeline according to the requested format at the pipeline input + and/or output. For complex devices, such as often found in embedded systems, identical image sizes at the output of a pipeline can be achieved using @@ -276,11 +277,11 @@
- Cropping and scaling + Selections: cropping, scaling and composition Many sub-devices support cropping frames on their input or output pads (or possible even on both). Cropping is used to select the area of - interest in an image, typically on a video sensor or video decoder. It can + interest in an image, typically on an image sensor or a video decoder. It can also be used as part of digital zoom implementations to select the area of the image that will be scaled up. @@ -288,26 +289,179 @@ &v4l2-rect; by the coordinates of the top left corner and the rectangle size. Both the coordinates and sizes are expressed in pixels. - The crop rectangle is retrieved and set using the - &VIDIOC-SUBDEV-G-CROP; and &VIDIOC-SUBDEV-S-CROP; ioctls. Like for pad - formats, drivers store try and active crop rectangles. The format - negotiation mechanism applies to crop settings as well. + As for pad formats, drivers store try and active + rectangles for the selection targets of ACTUAL type . - On input pads, cropping is applied relatively to the current pad - format. The pad format represents the image size as received by the - sub-device from the previous block in the pipeline, and the crop rectangle - represents the sub-image that will be transmitted further inside the - sub-device for processing. The crop rectangle be entirely containted - inside the input image size. + On sink pads, cropping is applied relative to the + current pad format. The pad format represents the image size as + received by the sub-device from the previous block in the + pipeline, and the crop rectangle represents the sub-image that + will be transmitted further inside the sub-device for + processing. - Input crop rectangle are reset to their default value when the input - image format is modified. Drivers should use the input image size as the - crop rectangle default value, but hardware requirements may prevent this. - + The scaling operation changes the size of the image by + scaling it to new dimensions. The scaling ratio isn't specified + explicitly, but is implied from the original and scaled image + sizes. Both sizes are represented by &v4l2-rect;. - Cropping behaviour on output pads is not defined. + Scaling support is optional. When supported by a subdev, + the crop rectangle on the subdev's sink pad is scaled to the + size configured using the &VIDIOC-SUBDEV-S-SELECTION; IOCTL + using V4L2_SUBDEV_SEL_COMPOSE_ACTUAL + selection target on the same pad. If the subdev supports scaling + but not composing, the top and left values are not used and must + always be set to zero. + + On source pads, cropping is similar to sink pads, with the + exception that the source size from which the cropping is + performed, is the COMPOSE rectangle on the sink pad. In both + sink and source pads, the crop rectangle must be entirely + contained inside the source image size for the crop + operation. + + The drivers should always use the closest possible + rectangle the user requests on all selection targets, unless + specifically told otherwise. + V4L2_SUBDEV_SEL_FLAG_SIZE_GE and + V4L2_SUBDEV_SEL_FLAG_SIZE_LE flags may be + used to round the image size either up or down. +
+ +
+ Types of selection targets + +
+ ACTUAL targets + + ACTUAL targets reflect the actual hardware configuration + at any point of time. There is a BOUNDS target + corresponding to every ACTUAL. +
+ +
+ BOUNDS targets + + BOUNDS targets is the smallest rectangle that contains + all valid ACTUAL rectangles. It may not be possible to set the + ACTUAL rectangle as large as the BOUNDS rectangle, however. + This may be because e.g. a sensor's pixel array is not + rectangular but cross-shaped or round. The maximum size may + also be smaller than the BOUNDS rectangle. +
+ +
+ Order of configuration and format propagation + + Inside subdevs, the order of image processing steps will + always be from the sink pad towards the source pad. This is also + reflected in the order in which the configuration must be + performed by the user: the changes made will be propagated to + any subsequent stages. If this behaviour is not desired, the + user must set + V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG flag. This + flag causes no propagation of the changes are allowed in any + circumstances. This may also cause the accessed rectangle to be + adjusted by the driver, depending on the properties of the + underlying hardware. + + The coordinates to a step always refer to the actual size + of the previous step. The exception to this rule is the source + compose rectangle, which refers to the sink compose bounds + rectangle --- if it is supported by the hardware. + + + Sink pad format. The user configures the sink pad + format. This format defines the parameters of the image the + entity receives through the pad for further processing. + + Sink pad actual crop selection. The sink pad crop + defines the crop performed to the sink pad format. + + Sink pad actual compose selection. The size of the + sink pad compose rectangle defines the scaling ratio compared + to the size of the sink pad crop rectangle. The location of + the compose rectangle specifies the location of the actual + sink compose rectangle in the sink compose bounds + rectangle. + + Source pad actual crop selection. Crop on the source + pad defines crop performed to the image in the sink compose + bounds rectangle. + + Source pad format. The source pad format defines the + output pixel format of the subdev, as well as the other + parameters with the exception of the image width and height. + Width and height are defined by the size of the source pad + actual crop selection. + + + Accessing any of the above rectangles not supported by the + subdev will return EINVAL. Any rectangle + referring to a previous unsupported rectangle coordinates will + instead refer to the previous supported rectangle. For example, + if sink crop is not supported, the compose selection will refer + to the sink pad format dimensions instead. + +
+ Image processing in subdevs: simple crop example + + + + + +
+ + In the above example, the subdev supports cropping on its + sink pad. To configure it, the user sets the media bus format on + the subdev's sink pad. Now the actual crop rectangle can be set + on the sink pad --- the location and size of this rectangle + reflect the location and size of a rectangle to be cropped from + the sink format. The size of the sink crop rectangle will also + be the size of the format of the subdev's source pad. + +
+ Image processing in subdevs: scaling with multiple sources + + + + + +
+ + In this example, the subdev is capable of first cropping, + then scaling and finally cropping for two source pads + individually from the resulting scaled image. The location of + the scaled image in the cropped image is ignored in sink compose + target. Both of the locations of the source crop rectangles + refer to the sink scaling rectangle, independently cropping an + area at location specified by the source crop rectangle from + it. + +
+ Image processing in subdevs: scaling and composition + with multiple sinks and sources + + + + + +
+ + The subdev driver supports two sink pads and two source + pads. The images from both of the sink pads are individually + cropped, then scaled and further composed on the composition + bounds rectangle. From that, two independent streams are cropped + and sent out of the subdev from the source pads. + +
+ &sub-subdev-formats; diff --git a/Documentation/DocBook/media/v4l/io.xml b/Documentation/DocBook/media/v4l/io.xml index b815929b5bba..fd6aca2922b6 100644 --- a/Documentation/DocBook/media/v4l/io.xml +++ b/Documentation/DocBook/media/v4l/io.xml @@ -543,12 +543,13 @@ and can range from zero to the number of buffers allocated with the &VIDIOC-REQBUFS; ioctl (&v4l2-requestbuffers; count) minus one.
- &v4l2-buf-type; + __u32 type Type of the buffer, same as &v4l2-format; type or &v4l2-requestbuffers; -type, set by the application. +type, set by the application. See __u32 @@ -568,7 +569,7 @@ refers to an input stream, applications when an output stream. linkend="buffer-flags" />. - &v4l2-field; + __u32 field Indicates the field order of the image in the @@ -630,11 +631,12 @@ bandwidth. These devices identify by not enumerating any video standards, see . - &v4l2-memory; + __u32 memory This field must be set by applications and/or drivers -in accordance with the selected I/O method. +in accordance with the selected I/O method. See union diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml index 7b274092e60c..c1c62a9acc2a 100644 --- a/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10.xml @@ -1,4 +1,4 @@ - + V4L2_PIX_FMT_SRGGB10 ('RG10'), V4L2_PIX_FMT_SGRBG10 ('BA10'), diff --git a/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml b/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml new file mode 100644 index 000000000000..8eace3e2e7d4 --- /dev/null +++ b/Documentation/DocBook/media/v4l/pixfmt-srggb10dpcm8.xml @@ -0,0 +1,29 @@ + + + + V4L2_PIX_FMT_SBGGR10DPCM8 ('bBA8'), + V4L2_PIX_FMT_SGBRG10DPCM8 ('bGA8'), + V4L2_PIX_FMT_SGRBG10DPCM8 ('BD10'), + V4L2_PIX_FMT_SRGGB10DPCM8 ('bRA8'), + + &manvol; + + + V4L2_PIX_FMT_SBGGR10DPCM8 + V4L2_PIX_FMT_SGBRG10DPCM8 + V4L2_PIX_FMT_SGRBG10DPCM8 + V4L2_PIX_FMT_SRGGB10DPCM8 + 10-bit Bayer formats compressed to 8 bits + + + Description + + The following four pixel formats are raw sRGB / Bayer formats + with 10 bits per colour compressed to 8 bits each, using DPCM + compression. DPCM, differential pulse-code modulation, is lossy. + Each colour component consumes 8 bits of memory. In other respects + this format is similar to . + + + diff --git a/Documentation/DocBook/media/v4l/pixfmt.xml b/Documentation/DocBook/media/v4l/pixfmt.xml index 31eaae2469f9..f5ac15ed0549 100644 --- a/Documentation/DocBook/media/v4l/pixfmt.xml +++ b/Documentation/DocBook/media/v4l/pixfmt.xml @@ -673,6 +673,7 @@ access the palette, this must be done with ioctls of the Linux framebuffer API.< &sub-srggb8; &sub-sbggr16; &sub-srggb10; + &sub-srggb10dpcm8; &sub-srggb12; @@ -876,11 +877,6 @@ kernel sources in the file Documentation/video4linux/cx2341x/README.hm 'S561' Compressed GBRG Bayer format used by the gspca driver. - - V4L2_PIX_FMT_SGRBG10DPCM8 - 'DB10' - 10 bit raw Bayer DPCM compressed to 8 bits. - V4L2_PIX_FMT_PAC207 'P207' diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-crop.dia b/Documentation/DocBook/media/v4l/subdev-image-processing-crop.dia new file mode 100644 index 000000000000..e32ba5362e1d --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-crop.dia @@ -0,0 +1,614 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 1 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 0 (sink)# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-crop.svg b/Documentation/DocBook/media/v4l/subdev-image-processing-crop.svg new file mode 100644 index 000000000000..18b0f5de9ed2 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-crop.svg @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + sink + crop + selection + + + + + + sink media + bus format + + + source media + bus format + + + + + + + + + + + + + + + + + + + + + pad 1 (source) + + + + + + + + + + + + + pad 0 (sink) + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-full.dia b/Documentation/DocBook/media/v4l/subdev-image-processing-full.dia new file mode 100644 index 000000000000..a0d782927840 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-full.dia @@ -0,0 +1,1588 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 0 (sink)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 2 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink compose +selection (scaling)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink compose +bounds selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 1 (sink)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 3 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-full.svg b/Documentation/DocBook/media/v4l/subdev-image-processing-full.svg new file mode 100644 index 000000000000..3322cf4c0093 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-full.svg @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pad 0 (sink) + + + pad 2 (source) + + + + + + + + + + + + + + sink media + bus format + + + + + + + + + + + sink compose + selection (scaling) + + + + + + + source media + bus format + + + + + + + + + + + sink compose + bounds selection + + + + + + + + + + + + + pad 1 (sink) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + pad 3 (source) + + + sink + crop + selection + + + source + crop + selection + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.dia b/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.dia new file mode 100644 index 000000000000..0cd50a7bda80 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.dia @@ -0,0 +1,1152 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #sink compose +selection (scaling)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source +crop +selection# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #source media +bus format# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 1 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 0 (sink)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pad 2 (source)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.svg b/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.svg new file mode 100644 index 000000000000..2340c0f8bc92 --- /dev/null +++ b/Documentation/DocBook/media/v4l/subdev-image-processing-scaling-multi-source.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + sink + crop + selection + + + + + + sink media + bus format + + + + + + + + + + + sink compose + selection (scaling) + + + + + + + source + crop + selection + + + source media + bus format + + + + + + + + + + + + + + + + + + + + + pad 1 (source) + + + + + + + + + + + + + pad 0 (sink) + + + + + + + + + + + + + + + + + + + + + + pad 2 (source) + + + + + + + + + + + + diff --git a/Documentation/DocBook/media/v4l/v4l2.xml b/Documentation/DocBook/media/v4l/v4l2.xml index 8ae38876172e..015c561754b7 100644 --- a/Documentation/DocBook/media/v4l/v4l2.xml +++ b/Documentation/DocBook/media/v4l/v4l2.xml @@ -28,8 +28,8 @@ documentation. Hans Verkuil Designed and documented the VIDIOC_LOG_STATUS ioctl, -the extended control ioctls and major parts of the sliced VBI -API. +the extended control ioctls, major parts of the sliced VBI API, the +MPEG encoder and decoder APIs and the DV Timings API.
hverkuil@xs4all.nl @@ -96,6 +96,17 @@ Remote Controller chapter.
+ + + Sakari + Ailus + Subdev selections API. + +
+ sakari.ailus@iki.fi +
+
+
@@ -112,6 +123,7 @@ Remote Controller chapter. 2009 2010 2011 + 2012 Bill Dirks, Michael H. Schimek, Hans Verkuil, Martin Rubli, Andy Walls, Muralidharan Karicheri, Mauro Carvalho Chehab, Pawel Osciak @@ -127,6 +139,28 @@ structs, ioctls) must be noted in more detail in the history chapter (compat.xml), along with the possible impact on existing drivers and applications. --> + + 3.5 + 2012-05-07 + sa, sn + Added V4L2_CTRL_TYPE_INTEGER_MENU and V4L2 subdev + selections API. Improved the description of V4L2_CID_COLORFX + control, added V4L2_CID_COLORFX_CBCR control. + Added camera controls V4L2_CID_AUTO_EXPOSURE_BIAS, + V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, V4L2_CID_IMAGE_STABILIZATION, + V4L2_CID_ISO_SENSITIVITY, V4L2_CID_ISO_SENSITIVITY_AUTO, + V4L2_CID_EXPOSURE_METERING, V4L2_CID_SCENE_MODE, + V4L2_CID_3A_LOCK, V4L2_CID_AUTO_FOCUS_START, + V4L2_CID_AUTO_FOCUS_STOP, V4L2_CID_AUTO_FOCUS_STATUS + and V4L2_CID_AUTO_FOCUS_RANGE. + + 2012-05-01 + hv + Added VIDIOC_ENUM_DV_TIMINGS, VIDIOC_QUERY_DV_TIMINGS and + VIDIOC_DV_TIMINGS_CAP. + + + 3.4 2012-01-25 @@ -433,7 +467,7 @@ and discussions on the V4L mailing list. Video for Linux Two API Specification - Revision 3.3 + Revision 3.5 &sub-common; @@ -491,10 +525,12 @@ and discussions on the V4L mailing list. &sub-dbg-g-register; &sub-decoder-cmd; &sub-dqevent; + &sub-dv-timings-cap; &sub-encoder-cmd; &sub-enumaudio; &sub-enumaudioout; &sub-enum-dv-presets; + &sub-enum-dv-timings; &sub-enum-fmt; &sub-enum-framesizes; &sub-enum-frameintervals; @@ -529,6 +565,7 @@ and discussions on the V4L mailing list. &sub-querycap; &sub-queryctrl; &sub-query-dv-preset; + &sub-query-dv-timings; &sub-querystd; &sub-prepare-buf; &sub-reqbufs; @@ -540,6 +577,7 @@ and discussions on the V4L mailing list. &sub-subdev-g-crop; &sub-subdev-g-fmt; &sub-subdev-g-frame-interval; + &sub-subdev-g-selection; &sub-subscribe-event; &sub-mmap; diff --git a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml index 73ae8a6cd004..765549ff8a71 100644 --- a/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-create-bufs.xml @@ -48,6 +48,12 @@ Description + + Experimental + This is an experimental + interface and may change in the future. + + This ioctl is used to create buffers for memory mapped or user pointer I/O. It can be used as an alternative or in addition to the @@ -94,16 +100,18 @@ information. The number of buffers requested or granted.
- &v4l2-memory; + __u32 memory Applications set this field to V4L2_MEMORY_MMAP or -V4L2_MEMORY_USERPTR. +V4L2_MEMORY_USERPTR. See - &v4l2-format; + __u32 format - Filled in by the application, preserved by the driver. + Filled in by the application, preserved by the driver. + See . __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml index b4f2f255211e..f1bac2c6e978 100644 --- a/Documentation/DocBook/media/v4l/vidioc-cropcap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-cropcap.xml @@ -65,7 +65,7 @@ output. &cs-str; - &v4l2-buf-type; + __u32 type Type of the data stream, set by the application. Only these types are valid here: @@ -73,7 +73,7 @@ Only these types are valid here: V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OVERLAY, and custom (driver defined) types with code V4L2_BUF_TYPE_PRIVATE -and higher. +and higher. See . struct v4l2_rect diff --git a/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml new file mode 100644 index 000000000000..6673ce582050 --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-dv-timings-cap.xml @@ -0,0 +1,211 @@ + + + ioctl VIDIOC_DV_TIMINGS_CAP + &manvol; + + + + VIDIOC_DV_TIMINGS_CAP + The capabilities of the Digital Video receiver/transmitter + + + + + + int ioctl + int fd + int request + struct v4l2_dv_timings_cap *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_DV_TIMINGS_CAP + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + To query the available timings, applications initialize the +index field and zero the reserved array of &v4l2-dv-timings-cap; +and call the VIDIOC_DV_TIMINGS_CAP ioctl with a pointer to this +structure. Drivers fill the rest of the structure or return an +&EINVAL; when the index is out of bounds. To enumerate all supported DV timings, +applications shall begin at index zero, incrementing by one until the +driver returns EINVAL. Note that drivers may enumerate a +different set of DV timings after switching the video input or +output. + + + struct <structname>v4l2_bt_timings_cap</structname> + + &cs-str; + + + __u32 + min_width + Minimum width of the active video in pixels. + + + __u32 + max_width + Maximum width of the active video in pixels. + + + __u32 + min_height + Minimum height of the active video in lines. + + + __u32 + max_height + Maximum height of the active video in lines. + + + __u64 + min_pixelclock + Minimum pixelclock frequency in Hz. + + + __u64 + max_pixelclock + Maximum pixelclock frequency in Hz. + + + __u32 + standards + The video standard(s) supported by the hardware. + See for a list of standards. + + + __u32 + capabilities + Several flags giving more information about the capabilities. + See for a description of the flags. + + + + __u32 + reserved[16] + + + + +
+ + + struct <structname>v4l2_dv_timings_cap</structname> + + &cs-str; + + + __u32 + type + Type of DV timings as listed in . + + + __u32 + reserved[3] + Reserved for future extensions. Drivers must set the array to zero. + + + union + + + + + + &v4l2-bt-timings-cap; + bt + BT.656/1120 timings capabilities of the hardware. + + + + __u32 + raw_data[32] + + + + +
+ + + DV BT Timing capabilities + + &cs-str; + + + Flag + Description + + + + + + + V4L2_DV_BT_CAP_INTERLACED + Interlaced formats are supported. + + + + V4L2_DV_BT_CAP_PROGRESSIVE + Progressive formats are supported. + + + + V4L2_DV_BT_CAP_REDUCED_BLANKING + CVT/GTF specific: the timings can make use of reduced blanking (CVT) +or the 'Secondary GTF' curve (GTF). + + + + V4L2_DV_BT_CAP_CUSTOM + Can support non-standard timings, i.e. timings not belonging to the +standards set in the standards field. + + + + +
+
+ + + &return-value; + +
+ + diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml index 0be17c232d3a..509f0012d2a6 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-presets.xml @@ -48,6 +48,10 @@ Description + This ioctl is deprecated. + New drivers and applications should use &VIDIOC-ENUM-DV-TIMINGS; instead. + + To query the attributes of a DV preset, applications initialize the index field and zero the reserved array of &v4l2-dv-enum-preset; and call the VIDIOC_ENUM_DV_PRESETS ioctl with a pointer to this diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml new file mode 100644 index 000000000000..24c3bf4fd29a --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-enum-dv-timings.xml @@ -0,0 +1,119 @@ + + + ioctl VIDIOC_ENUM_DV_TIMINGS + &manvol; + + + + VIDIOC_ENUM_DV_TIMINGS + Enumerate supported Digital Video timings + + + + + + int ioctl + int fd + int request + struct v4l2_enum_dv_timings *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_ENUM_DV_TIMINGS + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + While some DV receivers or transmitters support a wide range of timings, others +support only a limited number of timings. With this ioctl applications can enumerate a list +of known supported timings. Call &VIDIOC-DV-TIMINGS-CAP; to check if it also supports other +standards or even custom timings that are not in this list. + + To query the available timings, applications initialize the +index field and zero the reserved array of &v4l2-enum-dv-timings; +and call the VIDIOC_ENUM_DV_TIMINGS ioctl with a pointer to this +structure. Drivers fill the rest of the structure or return an +&EINVAL; when the index is out of bounds. To enumerate all supported DV timings, +applications shall begin at index zero, incrementing by one until the +driver returns EINVAL. Note that drivers may enumerate a +different set of DV timings after switching the video input or +output. + + + struct <structname>v4l2_enum_dv_timings</structname> + + &cs-str; + + + __u32 + index + Number of the DV timings, set by the +application. + + + __u32 + reserved[3] + Reserved for future extensions. Drivers must set the array to zero. + + + &v4l2-dv-timings; + timings + The timings. + + + +
+
+ + + &return-value; + + + + EINVAL + + The &v4l2-enum-dv-timings; index +is out of bounds. + + + + +
+ + diff --git a/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml index 347d142e7431..81ebe48317fe 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enum-fmt.xml @@ -71,7 +71,7 @@ the application. This is in no way related to the pixelformat field.
- &v4l2-buf-type; + __u32 type Type of the data stream, set by the application. Only these types are valid here: @@ -81,7 +81,7 @@ Only these types are valid here: V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_BUF_TYPE_VIDEO_OVERLAY, and custom (driver defined) types with code V4L2_BUF_TYPE_PRIVATE -and higher. +and higher. See . __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-enuminput.xml b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml index 9b8efcd6e947..46d5a044a537 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enuminput.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enuminput.xml @@ -285,7 +285,7 @@ input/output interface to linux-media@vger.kernel.org on 19 Oct 2009. V4L2_IN_CAP_CUSTOM_TIMINGS 0x00000002 - This input supports setting custom video timings by using VIDIOC_S_DV_TIMINGS. + This input supports setting video timings by using VIDIOC_S_DV_TIMINGS. V4L2_IN_CAP_STD diff --git a/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml index a64d5ef103fa..428020000ef0 100644 --- a/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml +++ b/Documentation/DocBook/media/v4l/vidioc-enumoutput.xml @@ -170,7 +170,7 @@ input/output interface to linux-media@vger.kernel.org on 19 Oct 2009. V4L2_OUT_CAP_CUSTOM_TIMINGS 0x00000002 - This output supports setting custom video timings by using VIDIOC_S_DV_TIMINGS. + This output supports setting video timings by using VIDIOC_S_DV_TIMINGS. V4L2_OUT_CAP_STD diff --git a/Documentation/DocBook/media/v4l/vidioc-g-crop.xml b/Documentation/DocBook/media/v4l/vidioc-g-crop.xml index 01a50640dce0..c4ff3b1887fb 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-crop.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-crop.xml @@ -100,14 +100,14 @@ changed and VIDIOC_S_CROP returns the &cs-str; - &v4l2-buf-type; + __u32 type Type of the data stream, set by the application. Only these types are valid here: V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OVERLAY, and custom (driver defined) types with code V4L2_BUF_TYPE_PRIVATE -and higher. +and higher. See . &v4l2-rect; diff --git a/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml index 7940c1149393..61be9fa3803a 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-preset.xml @@ -48,6 +48,12 @@ Description + + These ioctls are deprecated. + New drivers and applications should use &VIDIOC-G-DV-TIMINGS; and &VIDIOC-S-DV-TIMINGS; + instead. + + To query and select the current DV preset, applications use the VIDIOC_G_DV_PRESET and VIDIOC_S_DV_PRESET ioctls which take a pointer to a &v4l2-dv-preset; type as argument. diff --git a/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml index 4a8648ae9a63..eda1a2991bbe 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-dv-timings.xml @@ -7,7 +7,7 @@ VIDIOC_G_DV_TIMINGS VIDIOC_S_DV_TIMINGS - Get or set custom DV timings for input or output + Get or set DV timings for input or output @@ -48,12 +48,15 @@ Description - To set custom DV timings for the input or output, applications use the -VIDIOC_S_DV_TIMINGS ioctl and to get the current custom timings, + To set DV timings for the input or output, applications use the +VIDIOC_S_DV_TIMINGS ioctl and to get the current timings, applications use the VIDIOC_G_DV_TIMINGS ioctl. The detailed timing information is filled in using the structure &v4l2-dv-timings;. These ioctls take a pointer to the &v4l2-dv-timings; structure as argument. If the ioctl is not supported or the timing values are not correct, the driver returns &EINVAL;. +The linux/v4l2-dv-timings.h header can be used to get the +timings of the formats in the and +standards. @@ -83,12 +86,13 @@ or the timing values are not correct, the driver returns &EINVAL;. __u32 width - Width of the active video in pixels + Width of the active video in pixels. __u32 height - Height of the active video in lines + Height of the active video frame in lines. So for interlaced formats the + height of the active video in each field is height/2. __u32 @@ -125,32 +129,52 @@ bit 0 (V4L2_DV_VSYNC_POS_POL) is for vertical sync polarity and bit 1 (V4L2_DV_H __u32 vfrontporch - Vertical front porch in lines + Vertical front porch in lines. For interlaced formats this refers to the + odd field (aka field 1). __u32 vsync - Vertical sync length in lines + Vertical sync length in lines. For interlaced formats this refers to the + odd field (aka field 1). __u32 vbackporch - Vertical back porch in lines + Vertical back porch in lines. For interlaced formats this refers to the + odd field (aka field 1). __u32 il_vfrontporch - Vertical front porch in lines for bottom field of interlaced field formats + Vertical front porch in lines for the even field (aka field 2) of + interlaced field formats. __u32 il_vsync - Vertical sync length in lines for bottom field of interlaced field formats + Vertical sync length in lines for the even field (aka field 2) of + interlaced field formats. __u32 il_vbackporch - Vertical back porch in lines for bottom field of interlaced field formats + Vertical back porch in lines for the even field (aka field 2) of + interlaced field formats. + + + __u32 + standards + The video standard(s) this format belongs to. This will be filled in by + the driver. Applications must set this to 0. See + for a list of standards. + + + __u32 + flags + Several flags giving more information about the format. + See for a description of the flags. + @@ -211,6 +235,90 @@ bit 0 (V4L2_DV_VSYNC_POS_POL) is for vertical sync polarity and bit 1 (V4L2_DV_H + + DV BT Timing standards + + &cs-str; + + + Timing standard + Description + + + + + + + V4L2_DV_BT_STD_CEA861 + The timings follow the CEA-861 Digital TV Profile standard + + + V4L2_DV_BT_STD_DMT + The timings follow the VESA Discrete Monitor Timings standard + + + V4L2_DV_BT_STD_CVT + The timings follow the VESA Coordinated Video Timings standard + + + V4L2_DV_BT_STD_GTF + The timings follow the VESA Generalized Timings Formula standard + + + +
+ + DV BT Timing flags + + &cs-str; + + + Flag + Description + + + + + + + V4L2_DV_FL_REDUCED_BLANKING + CVT/GTF specific: the timings use reduced blanking (CVT) or the 'Secondary +GTF' curve (GTF). In both cases the horizontal and/or vertical blanking +intervals are reduced, allowing a higher resolution over the same +bandwidth. This is a read-only flag, applications must not set this. + + + + V4L2_DV_FL_CAN_REDUCE_FPS + CEA-861 specific: set for CEA-861 formats with a framerate that is a multiple +of six. These formats can be optionally played at 1 / 1.001 speed to +be compatible with 60 Hz based standards such as NTSC and PAL-M that use a framerate of +29.97 frames per second. If the transmitter can't generate such frequencies, then the +flag will also be cleared. This is a read-only flag, applications must not set this. + + + + V4L2_DV_FL_REDUCED_FPS + CEA-861 specific: only valid for video transmitters, the flag is cleared +by receivers. It is also only valid for formats with the V4L2_DV_FL_CAN_REDUCE_FPS flag +set, for other formats the flag will be cleared by the driver. + +If the application sets this flag, then the pixelclock used to set up the transmitter is +divided by 1.001 to make it compatible with NTSC framerates. If the transmitter +can't generate such frequencies, then the flag will also be cleared. + + + + V4L2_DV_FL_HALF_LINE + Specific to interlaced formats: if set, then field 1 (aka the odd field) +is really one half-line longer and field 2 (aka the even field) is really one half-line +shorter, so each field has exactly the same number of half-lines. Whether half-lines can be +detected or used depends on the hardware. + + + + +
&return-value; diff --git a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml index b17a7aac6997..e3d5afcdafbb 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-ext-ctrls.xml @@ -265,6 +265,32 @@ These controls are described in .
+ + V4L2_CTRL_CLASS_JPEG + 0x9d0000 + The class containing JPEG compression controls. +These controls are described in . + + + V4L2_CTRL_CLASS_IMAGE_SOURCE + 0x9e0000 The class containing image + source controls. These controls are described in . + + + V4L2_CTRL_CLASS_IMAGE_PROC + 0x9f0000 The class containing image + processing controls. These controls are described in . + + + V4L2_CTRL_CLASS_JPEG + 0x9d0000 + The class containing JPEG compression controls. +These controls are described in . + diff --git a/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml index 17fbda15137b..52acff193a6f 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-fmt.xml @@ -116,7 +116,7 @@ this ioctl. - &v4l2-buf-type; + __u32 type Type of the data stream, see modulator field and the &v4l2-modulator; index field. - &v4l2-tuner-type; + __u32 type The tuner type. This is the same value as in the -&v4l2-tuner; type field. The type must be set +&v4l2-tuner; type field. See The type must be set to V4L2_TUNER_RADIO for /dev/radioX device nodes, and to V4L2_TUNER_ANALOG_TV for all others. The field is not applicable to modulators, &ie; ignored -by drivers. +by drivers. See __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-g-parm.xml b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml index 19b1d85dd668..f83d2cdd1185 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-parm.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-parm.xml @@ -75,11 +75,12 @@ devices. &cs-ustr; - &v4l2-buf-type; + __u32 type The buffer (stream) type, same as &v4l2-format; -type, set by the application. +type, set by the application. See union diff --git a/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml b/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml index 71741daaf725..bd015d1563ff 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-sliced-vbi-cap.xml @@ -148,7 +148,7 @@ using the &VIDIOC-S-FMT; ioctl as described in service_lines[1][0] to zero. - &v4l2-buf-type; + __u32 type Type of the data stream, see . Should be diff --git a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml index 91ec2fb658f8..62a1aa200a36 100644 --- a/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml +++ b/Documentation/DocBook/media/v4l/vidioc-g-tuner.xml @@ -107,7 +107,7 @@ user. - &v4l2-tuner-type; + __u32 type Type of the tuner, see . diff --git a/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml b/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml index 7bde698760e4..fa7ad7e33228 100644 --- a/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml +++ b/Documentation/DocBook/media/v4l/vidioc-prepare-buf.xml @@ -48,6 +48,12 @@ Description + + Experimental + This is an experimental + interface and may change in the future. + + Applications can optionally call the VIDIOC_PREPARE_BUF ioctl to pass ownership of the buffer to the driver before actually enqueuing it, using the diff --git a/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml index 23b17f604211..1bc8aeb3ff1f 100644 --- a/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml +++ b/Documentation/DocBook/media/v4l/vidioc-query-dv-preset.xml @@ -49,6 +49,10 @@ input Description + This ioctl is deprecated. + New drivers and applications should use &VIDIOC-QUERY-DV-TIMINGS; instead. + + The hardware may be able to detect the current DV preset automatically, similar to sensing the video standard. To do so, applications call VIDIOC_QUERY_DV_PRESET with a pointer to a diff --git a/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml b/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml new file mode 100644 index 000000000000..44935a0ffcf0 --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-query-dv-timings.xml @@ -0,0 +1,104 @@ + + + ioctl VIDIOC_QUERY_DV_TIMINGS + &manvol; + + + + VIDIOC_QUERY_DV_TIMINGS + Sense the DV preset received by the current +input + + + + + + int ioctl + int fd + int request + struct v4l2_dv_timings *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_QUERY_DV_TIMINGS + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + The hardware may be able to detect the current DV timings +automatically, similar to sensing the video standard. To do so, applications +call VIDIOC_QUERY_DV_TIMINGS with a pointer to a +&v4l2-dv-timings;. Once the hardware detects the timings, it will fill in the +timings structure. + +If the timings could not be detected because there was no signal, then +ENOLINK is returned. If a signal was detected, but +it was unstable and the receiver could not lock to the signal, then +ENOLCK is returned. If the receiver could lock to the signal, +but the format is unsupported (e.g. because the pixelclock is out of range +of the hardware capabilities), then the driver fills in whatever timings it +could find and returns ERANGE. In that case the application +can call &VIDIOC-DV-TIMINGS-CAP; to compare the found timings with the hardware's +capabilities in order to give more precise feedback to the user. + + + + + &return-value; + + + + ENOLINK + + No timings could be detected because no signal was found. + + + + + ENOLCK + + The signal was unstable and the hardware could not lock on to it. + + + + + ERANGE + + Timings were found, but they are out of range of the hardware +capabilities. + + + + + + diff --git a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml index 36660d311b51..e6645b996558 100644 --- a/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml +++ b/Documentation/DocBook/media/v4l/vidioc-queryctrl.xml @@ -127,7 +127,7 @@ the first control with a higher ID. Drivers which do not support this flag yet always return an &EINVAL;. - &v4l2-ctrl-type; + __u32 type Type of control, see . @@ -215,11 +215,12 @@ the array to zero. struct <structname>v4l2_querymenu</structname> - + &cs-str; __u32 + id Identifies the control, set by the application from the respective &v4l2-queryctrl; @@ -227,18 +228,38 @@ from the respective &v4l2-queryctrl; __u32 + index Index of the menu item, starting at zero, set by the application. + union + + + + + + __u8 name[32] Name of the menu item, a NUL-terminated ASCII -string. This information is intended for the user. +string. This information is intended for the user. This field is valid +for V4L2_CTRL_FLAG_MENU type controls. + + + + __s64 + value + + Value of the integer menu item. This field is valid for + V4L2_CTRL_FLAG_INTEGER_MENU type + controls. + __u32 + reserved Reserved for future extensions. Drivers must set the array to zero. @@ -291,6 +312,20 @@ values which are actually different on the hardware. the menu items can be enumerated with the VIDIOC_QUERYMENU ioctl. + + V4L2_CTRL_TYPE_INTEGER_MENU + ≥ 0 + 1 + N-1 + + The control has a menu of N choices. The values of the + menu items can be enumerated with the + VIDIOC_QUERYMENU ioctl. This is + similar to V4L2_CTRL_TYPE_MENU + except that instead of strings, the menu items are + signed 64-bit integers. + + V4L2_CTRL_TYPE_BITMASK 0 diff --git a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml index 7be4b1d29b90..d7c95057bc51 100644 --- a/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml +++ b/Documentation/DocBook/media/v4l/vidioc-reqbufs.xml @@ -92,18 +92,19 @@ streamoff.--> The number of buffers requested or granted. - &v4l2-buf-type; + __u32 type Type of the stream or buffers, this is the same as the &v4l2-format; type field. See for valid values. - &v4l2-memory; + __u32 memory Applications set this field to V4L2_MEMORY_MMAP or -V4L2_MEMORY_USERPTR. +V4L2_MEMORY_USERPTR. See . __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml index 18b1a8266f7c..407dfceb71f0 100644 --- a/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml +++ b/Documentation/DocBook/media/v4l/vidioc-s-hw-freq-seek.xml @@ -73,10 +73,11 @@ same value as in the &v4l2-input; tuner field and the &v4l2-tuner; index field. - &v4l2-tuner-type; + __u32 type The tuner type. This is the same value as in the -&v4l2-tuner; type field. +&v4l2-tuner; type field. See __u32 diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml index 06197323a8cc..4cddd788c589 100644 --- a/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-crop.xml @@ -58,9 +58,12 @@ Description - Experimental - This is an experimental - interface and may change in the future. + Obsolete + + This is an obsolete + interface and may be removed in the future. It is superseded by + the selection + API. To retrieve the current crop rectangle applications set the diff --git a/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml new file mode 100644 index 000000000000..208e9f0da3f3 --- /dev/null +++ b/Documentation/DocBook/media/v4l/vidioc-subdev-g-selection.xml @@ -0,0 +1,228 @@ + + + ioctl VIDIOC_SUBDEV_G_SELECTION, VIDIOC_SUBDEV_S_SELECTION + &manvol; + + + + VIDIOC_SUBDEV_G_SELECTION + VIDIOC_SUBDEV_S_SELECTION + Get or set selection rectangles on a subdev pad + + + + + + int ioctl + int fd + int request + struct v4l2_subdev_selection *argp + + + + + + Arguments + + + + fd + + &fd; + + + + request + + VIDIOC_SUBDEV_G_SELECTION, VIDIOC_SUBDEV_S_SELECTION + + + + argp + + + + + + + + + Description + + + Experimental + This is an experimental + interface and may change in the future. + + + The selections are used to configure various image + processing functionality performed by the subdevs which affect the + image size. This currently includes cropping, scaling and + composition. + + The selection API replaces the old subdev crop API. All + the function of the crop API, and more, are supported by the + selections API. + + See for + more information on how each selection target affects the image + processing pipeline inside the subdevice. + +
+ Types of selection targets + + There are two types of selection targets: actual and bounds. + The ACTUAL targets are the targets which configure the hardware. + The BOUNDS target will return a rectangle that contain all + possible ACTUAL rectangles. +
+ +
+ Discovering supported features + + To discover which targets are supported, the user can + perform VIDIOC_SUBDEV_G_SELECTION on them. + Any unsupported target will return + EINVAL. +
+ +
+ V4L2 subdev selection targets + + &cs-def; + + + V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL + 0x0000 + Actual crop. Defines the cropping + performed by the processing step. + + + V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS + 0x0002 + Bounds of the crop rectangle. + + + V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL + 0x0100 + Actual compose rectangle. Used to configure scaling + on sink pads and composition on source pads. + + + V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS + 0x0102 + Bounds of the compose rectangle. + + + +
+ + + V4L2 subdev selection flags + + &cs-def; + + + V4L2_SUBDEV_SEL_FLAG_SIZE_GE + (1 << 0) Suggest the driver it + should choose greater or equal rectangle (in size) than + was requested. Albeit the driver may choose a lesser size, + it will only do so due to hardware limitations. Without + this flag (and + V4L2_SUBDEV_SEL_FLAG_SIZE_LE) the + behaviour is to choose the closest possible + rectangle. + + + V4L2_SUBDEV_SEL_FLAG_SIZE_LE + (1 << 1) Suggest the driver it + should choose lesser or equal rectangle (in size) than was + requested. Albeit the driver may choose a greater size, it + will only do so due to hardware limitations. + + + V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG + (1 << 2) + The configuration should not be propagated to any + further processing steps. If this flag is not given, the + configuration is propagated inside the subdevice to all + further processing steps. + + + +
+ + + struct <structname>v4l2_subdev_selection</structname> + + &cs-str; + + + __u32 + which + Active or try selection, from + &v4l2-subdev-format-whence;. + + + __u32 + pad + Pad number as reported by the media framework. + + + __u32 + target + Target selection rectangle. See + .. + + + __u32 + flags + Flags. See + . + + + &v4l2-rect; + rect + Selection rectangle, in pixels. + + + __u32 + reserved[8] + Reserved for future extensions. Applications and drivers must + set the array to zero. + + + +
+ + + + + &return-value; + + + + EBUSY + + The selection rectangle can't be changed because the + pad is currently busy. This can be caused, for instance, by + an active video stream on the pad. The ioctl must not be + retried without performing another action to fix the problem + first. Only returned by + VIDIOC_SUBDEV_S_SELECTION + + + + EINVAL + + The &v4l2-subdev-selection; + pad references a non-existing + pad, the which field references a + non-existing format, or the selection target is not + supported on the given subdev pad. + + + + + diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware index d1d4a179a382..fbb241174486 100755 --- a/Documentation/dvb/get_dvb_firmware +++ b/Documentation/dvb/get_dvb_firmware @@ -28,7 +28,8 @@ use IO::Handle; "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", "af9015", "ngene", "az6027", "lme2510_lg", "lme2510c_s7395", "lme2510c_s7395_old", "drxk", "drxk_terratec_h5", - "drxk_hauppauge_hvr930c", "tda10071", "it9135", "it9137"); + "drxk_hauppauge_hvr930c", "tda10071", "it9135", "it9137", + "drxk_pctv"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -730,6 +731,23 @@ sub tda10071 { "$fwfile"; } +sub drxk_pctv { + my $sourcefile = "PCTV_460e_reference.zip"; + my $url = "ftp://ftp.pctvsystems.com/TV/driver/PCTV%2070e%2080e%20100e%20320e%20330e%20800e/"; + my $hash = "4403de903bf2593464c8d74bbc200a57"; + my $fwfile = "dvb-demod-drxk-pctv.fw"; + my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1); + + checkstandard(); + + wgetfile($sourcefile, $url . $sourcefile); + verify($sourcefile, $hash); + unzip($sourcefile, $tmpdir); + extract("$tmpdir/PCTV\ 70e\ 80e\ 100e\ 320e\ 330e\ 800e/32\ bit/emOEM.sys", 0x72b80, 42692, $fwfile); + + "$fwfile"; +} + # --------------------------------------------------------------- # Utilities diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt index 1e69a81e99d4..c59f6e59fc1e 100644 --- a/Documentation/feature-removal-schedule.txt +++ b/Documentation/feature-removal-schedule.txt @@ -549,6 +549,15 @@ Who: Sasikantha Babu ---------------------------- +What: remove bogus DV presets V4L2_DV_1080I29_97, V4L2_DV_1080I30 and + V4L2_DV_1080I25 +When: 3.6 +Why: These HDTV formats do not exist and were added by a confused mind + (that was me, to be precise...) +Who: Hans Verkuil + +---------------------------- + What: V4L2_CID_HCENTER, V4L2_CID_VCENTER V4L2 controls When: 3.7 Why: The V4L2_CID_VCENTER, V4L2_CID_HCENTER controls have been deprecated diff --git a/Documentation/media-framework.txt b/Documentation/media-framework.txt index 3a0f879533ce..802875413873 100644 --- a/Documentation/media-framework.txt +++ b/Documentation/media-framework.txt @@ -335,6 +335,9 @@ the media_entity pipe field. Calls to media_entity_pipeline_start() can be nested. The pipeline pointer must be identical for all nested calls to the function. +media_entity_pipeline_start() may return an error. In that case, it will +clean up any the changes it did by itself. + When stopping the stream, drivers must notify the entities with media_entity_pipeline_stop(struct media_entity *entity); @@ -351,3 +354,19 @@ If other operations need to be disallowed on streaming entities (such as changing entities configuration parameters) drivers can explicitly check the media_entity stream_count field to find out if an entity is streaming. This operation must be done with the media_device graph_mutex held. + + +Link validation +--------------- + +Link validation is performed by media_entity_pipeline_start() for any +entity which has sink pads in the pipeline. The +media_entity::link_validate() callback is used for that purpose. In +link_validate() callback, entity driver should check that the properties of +the source pad of the connected entity and its own sink pad match. It is up +to the type of the entity (and in the end, the properties of the hardware) +what matching actually means. + +Subsystems should facilitate link validation by providing subsystem specific +helper functions to provide easy access for commonly needed information, and +in the end provide a way to use driver-specific callbacks. diff --git a/Documentation/video4linux/4CCs.txt b/Documentation/video4linux/4CCs.txt new file mode 100644 index 000000000000..41241af1ebfe --- /dev/null +++ b/Documentation/video4linux/4CCs.txt @@ -0,0 +1,32 @@ +Guidelines for Linux4Linux pixel format 4CCs +============================================ + +Guidelines for Video4Linux 4CC codes defined using v4l2_fourcc() are +specified in this document. First of the characters defines the nature of +the pixel format, compression and colour space. The interpretation of the +other three characters depends on the first one. + +Existing 4CCs may not obey these guidelines. + +Formats +======= + +Raw bayer +--------- + +The following first characters are used by raw bayer formats: + + B: raw bayer, uncompressed + b: raw bayer, DPCM compressed + a: A-law compressed + u: u-law compressed + +2nd character: pixel order + B: BGGR + G: GBRG + g: GRBG + R: RGGB + +3rd character: uncompressed bits-per-pixel 0--9, A-- + +4th character: compressed bits-per-pixel 0--9, A-- diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt index e6c2842407a4..1e6b6531bbcc 100644 --- a/Documentation/video4linux/gspca.txt +++ b/Documentation/video4linux/gspca.txt @@ -276,6 +276,7 @@ pac7302 093a:2622 Genius Eye 312 pac7302 093a:2624 PAC7302 pac7302 093a:2625 Genius iSlim 310 pac7302 093a:2626 Labtec 2200 +pac7302 093a:2627 Genius FaceCam 300 pac7302 093a:2628 Genius iLook 300 pac7302 093a:2629 Genious iSlim 300 pac7302 093a:262a Webcam 300k diff --git a/Documentation/video4linux/v4l2-controls.txt b/Documentation/video4linux/v4l2-controls.txt index e2492a9d1027..43da22b89728 100644 --- a/Documentation/video4linux/v4l2-controls.txt +++ b/Documentation/video4linux/v4l2-controls.txt @@ -130,8 +130,18 @@ Menu controls are added by calling v4l2_ctrl_new_std_menu: const struct v4l2_ctrl_ops *ops, u32 id, s32 max, s32 skip_mask, s32 def); +Or alternatively for integer menu controls, by calling v4l2_ctrl_new_int_menu: + + struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 max, s32 def, const s64 *qmenu_int); + These functions are typically called right after the v4l2_ctrl_handler_init: + static const s64 exp_bias_qmenu[] = { + -2, -1, 0, 1, 2 + }; + v4l2_ctrl_handler_init(&foo->ctrl_handler, nr_of_controls); v4l2_ctrl_new_std(&foo->ctrl_handler, &foo_ctrl_ops, V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); @@ -141,6 +151,11 @@ These functions are typically called right after the v4l2_ctrl_handler_init: V4L2_CID_POWER_LINE_FREQUENCY, V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); + v4l2_ctrl_new_int_menu(&foo->ctrl_handler, &foo_ctrl_ops, + V4L2_CID_EXPOSURE_BIAS, + ARRAY_SIZE(exp_bias_qmenu) - 1, + ARRAY_SIZE(exp_bias_qmenu) / 2 - 1, + exp_bias_qmenu); ... if (foo->ctrl_handler.error) { int err = foo->ctrl_handler.error; @@ -164,6 +179,12 @@ controls. There is no min argument since that is always 0 for menu controls, and instead of a step there is a skip_mask argument: if bit X is 1, then menu item X is skipped. +The v4l2_ctrl_new_int_menu function creates a new standard integer menu +control with driver-specific items in the menu. It differs from +v4l2_ctrl_new_std_menu in that it doesn't have the mask argument and takes +as the last argument an array of signed 64-bit integers that form an exact +menu item list. + Note that if something fails, the function will return NULL or an error and set ctrl_handler->error to the error code. If ctrl_handler->error was already set, then it will just return and do nothing. This is also true for diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index 659b2ba12a4f..1f5905270050 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt @@ -182,11 +182,11 @@ static int __devinit drv_probe(struct pci_dev *pdev, } If you have multiple device nodes then it can be difficult to know when it is -safe to unregister v4l2_device. For this purpose v4l2_device has refcounting -support. The refcount is increased whenever video_register_device is called and -it is decreased whenever that device node is released. When the refcount reaches -zero, then the v4l2_device release() callback is called. You can do your final -cleanup there. +safe to unregister v4l2_device for hotpluggable devices. For this purpose +v4l2_device has refcounting support. The refcount is increased whenever +video_register_device is called and it is decreased whenever that device node +is released. When the refcount reaches zero, then the v4l2_device release() +callback is called. You can do your final cleanup there. If other device nodes (e.g. ALSA) are created, then you can increase and decrease the refcount manually as well by calling: @@ -197,6 +197,10 @@ or: int v4l2_device_put(struct v4l2_device *v4l2_dev); +Since the initial refcount is 1 you also need to call v4l2_device_put in the +disconnect() callback (for USB devices) or in the remove() callback (for e.g. +PCI devices), otherwise the refcount will never reach 0. + struct v4l2_subdev ------------------ @@ -262,11 +266,16 @@ struct v4l2_subdev_video_ops { ... }; +struct v4l2_subdev_pad_ops { + ... +}; + 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_pad_ops *video; }; The core ops are common to all subdevs, the other categories are implemented @@ -303,6 +312,22 @@ Don't forget to cleanup the media entity before the sub-device is destroyed: media_entity_cleanup(&sd->entity); +If the subdev driver intends to process video and integrate with the media +framework, it must implement format related functionality using +v4l2_subdev_pad_ops instead of v4l2_subdev_video_ops. + +In that case, the subdev driver may set the link_validate field to provide +its own link validation function. The link validation function is called for +every link in the pipeline where both of the ends of the links are V4L2 +sub-devices. The driver is still responsible for validating the correctness +of the format configuration between sub-devices and video nodes. + +If link_validate op is not set, the default function +v4l2_subdev_link_validate_default() is used instead. This function ensures +that width, height and the media bus pixel code are equal on both source and +sink of the link. Subdev drivers are also free to use this function to +perform the checks mentioned above in addition to their own checks. + A device (bridge) driver needs to register the v4l2_subdev with the v4l2_device: @@ -555,19 +580,25 @@ allocated memory. You should also set these fields: - v4l2_dev: set to the v4l2_device parent device. + - name: set to something descriptive and unique. + - fops: set to the v4l2_file_operations struct. + - ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance (highly recommended to use this and it might become compulsory in the future!), then set this to your v4l2_ioctl_ops struct. + - lock: leave to NULL if you want to do all the locking in the driver. - Otherwise you give it a pointer to a struct mutex_lock and before any - of the v4l2_file_operations is called this lock will be taken by the - core and released afterwards. + Otherwise you give it a pointer to a struct mutex_lock and before the + unlocked_ioctl file operation is called this lock will be taken by the + core and released afterwards. See the next section for more details. + - prio: keeps track of the priorities. Used to implement VIDIOC_G/S_PRIORITY. If left to NULL, then it will use the struct v4l2_prio_state in v4l2_device. If you want to have a separate priority state per (group of) device node(s), then you can point it to your own struct v4l2_prio_state. + - parent: you only set this if v4l2_device was registered with NULL as the parent device struct. This only happens in cases where one hardware device has multiple PCI devices that all share the same v4l2_device core. @@ -577,6 +608,7 @@ You should also set these fields: (cx8802). Since the v4l2_device cannot be associated with a particular PCI device it is setup without a parent device. But when the struct video_device is setup you do know which parent PCI device to use. + - flags: optional. Set to V4L2_FL_USE_FH_PRIO if you want to let the framework handle the VIDIOC_G/S_PRIORITY ioctls. This requires that you use struct v4l2_fh. Eventually this flag will disappear once all drivers use the core @@ -587,6 +619,16 @@ in your v4l2_file_operations struct. Do not use .ioctl! This is deprecated and will go away in the future. +In some cases you want to tell the core that a function you had specified in +your v4l2_ioctl_ops should be ignored. You can mark such ioctls by calling this +function before video_device_register is called: + +void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd); + +This tends to be needed if based on external factors (e.g. which card is +being used) you want to turns off certain features in v4l2_ioctl_ops without +having to make a new struct. + The v4l2_file_operations struct is a subset of file_operations. The main difference is that the inode argument is omitted since it is never used. @@ -609,8 +651,22 @@ v4l2_file_operations and locking -------------------------------- You can set a pointer to a mutex_lock in struct video_device. Usually this -will be either a top-level mutex or a mutex per device node. If you want -finer-grained locking then you have to set it to NULL and do you own locking. +will be either a top-level mutex or a mutex per device node. By default this +lock will be used for unlocked_ioctl, but you can disable locking for +selected ioctls by calling: + + void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd); + +E.g.: v4l2_disable_ioctl_locking(vdev, VIDIOC_DQBUF); + +You have to call this before you register the video_device. + +Particularly with USB drivers where certain commands such as setting controls +can take a long time you may want to do your own locking for the buffer queuing +ioctls. + +If you want still finer-grained locking then you have to set mutex_lock to NULL +and do you own locking completely. It is up to the driver developer to decide which method to use. However, if your driver has high-latency operations (for example, changing the exposure @@ -618,7 +674,7 @@ of a USB webcam might take a long time), then you might be better off with doing your own locking if you want to allow the user to do other things with the device while waiting for the high-latency command to finish. -If a lock is specified then all file operations will be serialized on that +If a lock is specified then all ioctl commands will be serialized on that lock. If you use videobuf then you must pass the same lock to the videobuf queue initialize function: if videobuf has to wait for a frame to arrive, then it will temporarily unlock the lock and relock it afterwards. If your driver @@ -941,21 +997,35 @@ fast. Useful functions: -- v4l2_event_queue() +void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) Queue events to video device. The driver's only responsibility is to fill in the type and the data fields. The other fields will be filled in by V4L2. -- v4l2_event_subscribe() +int v4l2_event_subscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub, unsigned elems, + const struct v4l2_subscribed_event_ops *ops) The video_device->ioctl_ops->vidioc_subscribe_event must check the driver is able to produce events with specified event id. Then it calls - v4l2_event_subscribe() to subscribe the event. The last argument is the - size of the event queue for this event. If it is 0, then the framework - will fill in a default value (this depends on the event type). + v4l2_event_subscribe() to subscribe the event. -- v4l2_event_unsubscribe() + The elems argument is the size of the event queue for this event. If it is 0, + then the framework will fill in a default value (this depends on the event + type). + + The ops argument allows the driver to specify a number of callbacks: + * add: called when a new listener gets added (subscribing to the same + event twice will only cause this callback to get called once) + * del: called when a listener stops listening + * replace: replace event 'old' with event 'new'. + * merge: merge event 'old' into event 'new'. + All 4 callbacks are optional, if you don't want to specify any callbacks + the ops argument itself maybe NULL. + +int v4l2_event_unsubscribe(struct v4l2_fh *fh, + struct v4l2_event_subscription *sub) vidioc_unsubscribe_event in struct v4l2_ioctl_ops. A driver may use v4l2_event_unsubscribe() directly unless it wants to be involved in @@ -964,7 +1034,7 @@ Useful functions: The special type V4L2_EVENT_ALL may be used to unsubscribe all events. The drivers may want to handle this in a special way. -- v4l2_event_pending() +int v4l2_event_pending(struct v4l2_fh *fh) Returns the number of pending events. Useful when implementing poll. diff --git a/MAINTAINERS b/MAINTAINERS index 27a1d3c6eec8..54c984e9d058 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2718,6 +2718,13 @@ S: Maintained F: Documentation/hwmon/f71805f F: drivers/hwmon/f71805f.c +FC0011 TUNER DRIVER +M: Michael Buesch +L: linux-media@vger.kernel.org +S: Maintained +F: drivers/media/common/tuners/fc0011.h +F: drivers/media/common/tuners/fc0011.c + FANOTIFY M: Eric Paris S: Maintained @@ -7191,7 +7198,7 @@ F: include/linux/usb/usbnet.h USB VIDEO CLASS M: Laurent Pinchart -L: linux-uvc-devel@lists.berlios.de (subscribers-only) +L: linux-uvc-devel@lists.sourceforge.net (subscribers-only) L: linux-media@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://www.ideasonboard.org/uvc/ diff --git a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c index 748ba2e311b5..dff82eb57cd9 100644 --- a/arch/arm/mach-imx/mach-imx27_visstrim_m10.c +++ b/arch/arm/mach-imx/mach-imx27_visstrim_m10.c @@ -178,7 +178,7 @@ static struct soc_camera_link iclink_tvp5150 = { static struct mx2_camera_platform_data visstrim_camera = { .flags = MX2_CAMERA_CCIR | MX2_CAMERA_CCIR_INTERLACE | - MX2_CAMERA_SWAP16 | MX2_CAMERA_PCLK_SAMPLE_RISING, + MX2_CAMERA_PCLK_SAMPLE_RISING, .clk = 100000, }; diff --git a/arch/arm/plat-mxc/include/mach/mx2_cam.h b/arch/arm/plat-mxc/include/mach/mx2_cam.h index 3c080a32dbf5..7ded6f1f74bc 100644 --- a/arch/arm/plat-mxc/include/mach/mx2_cam.h +++ b/arch/arm/plat-mxc/include/mach/mx2_cam.h @@ -23,7 +23,6 @@ #ifndef __MACH_MX2_CAM_H_ #define __MACH_MX2_CAM_H_ -#define MX2_CAMERA_SWAP16 (1 << 0) #define MX2_CAMERA_EXT_VSYNC (1 << 1) #define MX2_CAMERA_CCIR (1 << 2) #define MX2_CAMERA_CCIR_INTERLACE (1 << 3) @@ -31,7 +30,6 @@ #define MX2_CAMERA_GATED_CLOCK (1 << 5) #define MX2_CAMERA_INV_DATA (1 << 6) #define MX2_CAMERA_PCLK_SAMPLE_RISING (1 << 7) -#define MX2_CAMERA_PACK_DIR_MSB (1 << 8) /** * struct mx2_camera_platform_data - optional platform data for mx2_camera diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c index 117a59aaa70e..5f558851d646 100644 --- a/drivers/input/ff-memless.c +++ b/drivers/input/ff-memless.c @@ -31,8 +31,7 @@ #include #include #include - -#include "fixp-arith.h" +#include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Anssi Hannula "); diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c index 71f8e018e564..7d42c11c8684 100644 --- a/drivers/media/common/saa7146_fops.c +++ b/drivers/media/common/saa7146_fops.c @@ -198,7 +198,6 @@ static int fops_open(struct file *file) struct saa7146_dev *dev = video_drvdata(file); struct saa7146_fh *fh = NULL; int result = 0; - enum v4l2_buf_type type; DEB_EE("file:%p, dev:%s\n", file, video_device_node_name(vdev)); @@ -227,11 +226,12 @@ static int fops_open(struct file *file) goto out; } - file->private_data = fh; - fh->dev = dev; - fh->type = type; + v4l2_fh_init(&fh->fh, vdev); - if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + file->private_data = &fh->fh; + fh->dev = dev; + + if (vdev->vfl_type == VFL_TYPE_VBI) { DEB_S("initializing vbi...\n"); if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) result = saa7146_vbi_uops.open(dev,file); @@ -252,6 +252,7 @@ static int fops_open(struct file *file) } result = 0; + v4l2_fh_add(&fh->fh); out: if (fh && result != 0) { kfree(fh); @@ -263,6 +264,7 @@ out: static int fops_release(struct file *file) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct saa7146_dev *dev = fh->dev; @@ -271,7 +273,7 @@ static int fops_release(struct file *file) if (mutex_lock_interruptible(&saa7146_devices_lock)) return -ERESTARTSYS; - if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (vdev->vfl_type == VFL_TYPE_VBI) { if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) saa7146_vbi_uops.release(dev,file); if (dev->ext_vv_data->vbi_fops.release) @@ -280,6 +282,8 @@ static int fops_release(struct file *file) saa7146_video_uops.release(dev,file); } + v4l2_fh_del(&fh->fh); + v4l2_fh_exit(&fh->fh); module_put(dev->ext->module); file->private_data = NULL; kfree(fh); @@ -291,19 +295,22 @@ static int fops_release(struct file *file) static int fops_mmap(struct file *file, struct vm_area_struct * vma) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct videobuf_queue *q; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: { DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, vma:%p\n", file, vma); q = &fh->video_q; break; } - case V4L2_BUF_TYPE_VBI_CAPTURE: { + case VFL_TYPE_VBI: { DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, vma:%p\n", file, vma); + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return -ENODEV; q = &fh->vbi_q; break; } @@ -317,15 +324,19 @@ static int fops_mmap(struct file *file, struct vm_area_struct * vma) static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; struct videobuf_buffer *buf = NULL; struct videobuf_queue *q; + unsigned int res = v4l2_ctrl_poll(file, wait); DEB_EE("file:%p, poll:%p\n", file, wait); - if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type) { + if (vdev->vfl_type == VFL_TYPE_VBI) { + if (fh->dev->ext_vv_data->capabilities & V4L2_CAP_SLICED_VBI_OUTPUT) + return res | POLLOUT | POLLWRNORM; if( 0 == fh->vbi_q.streaming ) - return videobuf_poll_stream(file, &fh->vbi_q, wait); + return res | videobuf_poll_stream(file, &fh->vbi_q, wait); q = &fh->vbi_q; } else { DEB_D("using video queue\n"); @@ -337,31 +348,32 @@ static unsigned int fops_poll(struct file *file, struct poll_table_struct *wait) if (!buf) { DEB_D("buf == NULL!\n"); - return POLLERR; + return res | POLLERR; } poll_wait(file, &buf->done, wait); if (buf->state == VIDEOBUF_DONE || buf->state == VIDEOBUF_ERROR) { DEB_D("poll succeeded!\n"); - return POLLIN|POLLRDNORM; + return res | POLLIN | POLLRDNORM; } DEB_D("nothing to poll for, buf->state:%d\n", buf->state); - return 0; + return res; } static ssize_t fops_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: /* DEB_EE("V4L2_BUF_TYPE_VIDEO_CAPTURE: file:%p, data:%p, count:%lun", file, data, (unsigned long)count); */ return saa7146_video_uops.read(file,data,count,ppos); - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: /* DEB_EE("V4L2_BUF_TYPE_VBI_CAPTURE: file:%p, data:%p, count:%lu\n", file, data, (unsigned long)count); @@ -377,12 +389,13 @@ static ssize_t fops_read(struct file *file, char __user *data, size_t count, lof static ssize_t fops_write(struct file *file, const char __user *data, size_t count, loff_t *ppos) { + struct video_device *vdev = video_devdata(file); struct saa7146_fh *fh = file->private_data; - switch (fh->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: + switch (vdev->vfl_type) { + case VFL_TYPE_GRABBER: return -EINVAL; - case V4L2_BUF_TYPE_VBI_CAPTURE: + case VFL_TYPE_VBI: if (fh->dev->ext_vv_data->vbi_fops.write) return fh->dev->ext_vv_data->vbi_fops.write(file, data, count, ppos); else @@ -429,8 +442,15 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status) } } +static const struct v4l2_ctrl_ops saa7146_ctrl_ops = { + .s_ctrl = saa7146_s_ctrl, +}; + int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; + struct v4l2_pix_format *fmt; + struct v4l2_vbi_format *vbi; struct saa7146_vv *vv; int err; @@ -438,12 +458,32 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if (err) return err; + v4l2_ctrl_handler_init(hdl, 6); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_CONTRAST, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_SATURATION, 0, 127, 1, 64); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &saa7146_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + if (hdl->error) { + err = hdl->error; + v4l2_ctrl_handler_free(hdl); + return err; + } + dev->v4l2_dev.ctrl_handler = hdl; + vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL); if (vv == NULL) { ERR("out of memory. aborting.\n"); + v4l2_ctrl_handler_free(hdl); return -ENOMEM; } - ext_vv->ops = saa7146_video_ioctl_ops; + ext_vv->vid_ops = saa7146_video_ioctl_ops; + ext_vv->vbi_ops = saa7146_vbi_ioctl_ops; ext_vv->core_ops = &saa7146_video_ioctl_ops; DEB_EE("dev:%p\n", dev); @@ -463,6 +503,7 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if( NULL == vv->d_clipping.cpu_addr ) { ERR("out of memory. aborting.\n"); kfree(vv); + v4l2_ctrl_handler_free(hdl); return -1; } memset(vv->d_clipping.cpu_addr, 0x0, SAA7146_CLIPPING_MEM); @@ -471,6 +512,39 @@ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv) if (dev->ext_vv_data->capabilities & V4L2_CAP_VBI_CAPTURE) saa7146_vbi_uops.init(dev,vv); + fmt = &vv->ov_fb.fmt; + fmt->width = vv->standard->h_max_out; + fmt->height = vv->standard->v_max_out; + fmt->pixelformat = V4L2_PIX_FMT_RGB565; + fmt->bytesperline = 2 * fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height; + fmt->colorspace = V4L2_COLORSPACE_SRGB; + + fmt = &vv->video_fmt; + fmt->width = 384; + fmt->height = 288; + fmt->pixelformat = V4L2_PIX_FMT_BGR24; + fmt->field = V4L2_FIELD_ANY; + fmt->colorspace = V4L2_COLORSPACE_SMPTE170M; + fmt->bytesperline = 3 * fmt->width; + fmt->sizeimage = fmt->bytesperline * fmt->height; + + vbi = &vv->vbi_fmt; + vbi->sampling_rate = 27000000; + vbi->offset = 248; /* todo */ + vbi->samples_per_line = 720 * 2; + vbi->sample_format = V4L2_PIX_FMT_GREY; + + /* fixme: this only works for PAL */ + vbi->start[0] = 5; + vbi->count[0] = 16; + vbi->start[1] = 312; + vbi->count[1] = 16; + + init_timer(&vv->vbi_read_timeout); + + vv->ov_fb.capability = V4L2_FBUF_CAP_LIST_CLIPPING; + vv->ov_fb.flags = V4L2_FBUF_FLAG_PRIMARY; dev->vv_data = vv; dev->vv_callback = &vv_callback; @@ -486,6 +560,7 @@ int saa7146_vv_release(struct saa7146_dev* dev) v4l2_device_unregister(&dev->v4l2_dev); pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle); + v4l2_ctrl_handler_free(&dev->ctrl_handler); kfree(vv); dev->vv_data = NULL; dev->vv_callback = NULL; @@ -509,10 +584,19 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev, return -ENOMEM; vfd->fops = &video_fops; - vfd->ioctl_ops = &dev->ext_vv_data->ops; + if (type == VFL_TYPE_GRABBER) + vfd->ioctl_ops = &dev->ext_vv_data->vid_ops; + else + vfd->ioctl_ops = &dev->ext_vv_data->vbi_ops; vfd->release = video_device_release; + /* Locking in file operations other than ioctl should be done by + the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->v4l2_lock; + vfd->v4l2_dev = &dev->v4l2_dev; vfd->tvnorms = 0; + set_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); for (i = 0; i < dev->ext_vv_data->num_stds; i++) vfd->tvnorms |= dev->ext_vv_data->stds[i].id; strlcpy(vfd->name, name, sizeof(vfd->name)); diff --git a/drivers/media/common/saa7146_hlp.c b/drivers/media/common/saa7146_hlp.c index bc1f545c95cb..be746d1aee9a 100644 --- a/drivers/media/common/saa7146_hlp.c +++ b/drivers/media/common/saa7146_hlp.c @@ -343,9 +343,9 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa struct saa7146_vv *vv = dev->vv_data; __le32 *clipping = vv->d_clipping.cpu_addr; - int width = fh->ov.win.w.width; - int height = fh->ov.win.w.height; - int clipcount = fh->ov.nclips; + int width = vv->ov.win.w.width; + int height = vv->ov.win.w.height; + int clipcount = vv->ov.nclips; u32 line_list[32]; u32 pixel_list[32]; @@ -365,10 +365,10 @@ static void calculate_clipping_registers_rect(struct saa7146_dev *dev, struct sa for(i = 0; i < clipcount; i++) { int l = 0, r = 0, t = 0, b = 0; - x[i] = fh->ov.clips[i].c.left; - y[i] = fh->ov.clips[i].c.top; - w[i] = fh->ov.clips[i].c.width; - h[i] = fh->ov.clips[i].c.height; + x[i] = vv->ov.clips[i].c.left; + y[i] = vv->ov.clips[i].c.top; + w[i] = vv->ov.clips[i].c.width; + h[i] = vv->ov.clips[i].c.height; if( w[i] < 0) { x[i] += w[i]; w[i] = -w[i]; @@ -485,13 +485,14 @@ static void saa7146_disable_clipping(struct saa7146_dev *dev) static void saa7146_set_clipping_rect(struct saa7146_fh *fh) { struct saa7146_dev *dev = fh->dev; - enum v4l2_field field = fh->ov.win.field; + struct saa7146_vv *vv = dev->vv_data; + enum v4l2_field field = vv->ov.win.field; struct saa7146_video_dma vdma2; u32 clip_format; u32 arbtr_ctrl; /* check clipcount, disable clipping if clipcount == 0*/ - if( fh->ov.nclips == 0 ) { + if (vv->ov.nclips == 0) { saa7146_disable_clipping(dev); return; } @@ -651,8 +652,8 @@ int saa7146_enable_overlay(struct saa7146_fh *fh) struct saa7146_dev *dev = fh->dev; struct saa7146_vv *vv = dev->vv_data; - saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field); - saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field, vv->ov_fmt->pixelformat); + saa7146_set_window(dev, vv->ov.win.w.width, vv->ov.win.w.height, vv->ov.win.field); + saa7146_set_position(dev, vv->ov.win.w.left, vv->ov.win.w.top, vv->ov.win.w.height, vv->ov.win.field, vv->ov_fmt->pixelformat); saa7146_set_output_format(dev, vv->ov_fmt->trans); saa7146_set_clipping_rect(fh); diff --git a/drivers/media/common/saa7146_vbi.c b/drivers/media/common/saa7146_vbi.c index b2e718343739..1e71e374bbfe 100644 --- a/drivers/media/common/saa7146_vbi.c +++ b/drivers/media/common/saa7146_vbi.c @@ -211,7 +211,7 @@ static int buffer_activate(struct saa7146_dev *dev, DEB_VBI("dev:%p, buf:%p, next:%p\n", dev, buf, next); saa7146_set_vbi_capture(dev,buf,next); - mod_timer(&vv->vbi_q.timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->vbi_dmaq.timeout, jiffies+BUFFER_TIMEOUT); return 0; } @@ -294,7 +294,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_VBI("vb:%p\n", vb); - saa7146_buffer_queue(dev,&vv->vbi_q,buf); + saa7146_buffer_queue(dev, &vv->vbi_dmaq, buf); } static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) @@ -335,16 +335,15 @@ static void vbi_stop(struct saa7146_fh *fh, struct file *file) /* shut down dma 3 transfers */ saa7146_write(dev, MC1, MASK_20); - if (vv->vbi_q.curr) { - saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE); - } + if (vv->vbi_dmaq.curr) + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); videobuf_queue_cancel(&fh->vbi_q); vv->vbi_streaming = NULL; - del_timer(&vv->vbi_q.timeout); - del_timer(&fh->vbi_read_timeout); + del_timer(&vv->vbi_dmaq.timeout); + del_timer(&vv->vbi_read_timeout); spin_unlock_irqrestore(&dev->slock, flags); } @@ -364,12 +363,12 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) { DEB_VBI("dev:%p\n", dev); - INIT_LIST_HEAD(&vv->vbi_q.queue); + INIT_LIST_HEAD(&vv->vbi_dmaq.queue); - init_timer(&vv->vbi_q.timeout); - vv->vbi_q.timeout.function = saa7146_buffer_timeout; - vv->vbi_q.timeout.data = (unsigned long)(&vv->vbi_q); - vv->vbi_q.dev = dev; + init_timer(&vv->vbi_dmaq.timeout); + vv->vbi_dmaq.timeout.function = saa7146_buffer_timeout; + vv->vbi_dmaq.timeout.data = (unsigned long)(&vv->vbi_dmaq); + vv->vbi_dmaq.dev = dev; init_waitqueue_head(&vv->vbi_wq); } @@ -377,6 +376,7 @@ static void vbi_init(struct saa7146_dev *dev, struct saa7146_vv *vv) static int vbi_open(struct saa7146_dev *dev, struct file *file) { struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; u32 arbtr_ctrl = saa7146_read(dev, PCI_BT_V1); int ret = 0; @@ -395,19 +395,6 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) saa7146_write(dev, PCI_BT_V1, arbtr_ctrl); saa7146_write(dev, MC2, (MASK_04|MASK_20)); - memset(&fh->vbi_fmt,0,sizeof(fh->vbi_fmt)); - - fh->vbi_fmt.sampling_rate = 27000000; - fh->vbi_fmt.offset = 248; /* todo */ - fh->vbi_fmt.samples_per_line = vbi_pixel_to_capture; - fh->vbi_fmt.sample_format = V4L2_PIX_FMT_GREY; - - /* fixme: this only works for PAL */ - fh->vbi_fmt.start[0] = 5; - fh->vbi_fmt.count[0] = 16; - fh->vbi_fmt.start[1] = 312; - fh->vbi_fmt.count[1] = 16; - videobuf_queue_sg_init(&fh->vbi_q, &vbi_qops, &dev->pci->dev, &dev->slock, V4L2_BUF_TYPE_VBI_CAPTURE, @@ -415,9 +402,8 @@ static int vbi_open(struct saa7146_dev *dev, struct file *file) sizeof(struct saa7146_buf), file, &dev->v4l2_lock); - init_timer(&fh->vbi_read_timeout); - fh->vbi_read_timeout.function = vbi_read_timeout; - fh->vbi_read_timeout.data = (unsigned long)file; + vv->vbi_read_timeout.function = vbi_read_timeout; + vv->vbi_read_timeout.data = (unsigned long)file; /* initialize the brs */ if ( 0 != (SAA7146_USE_PORT_B_FOR_VBI & dev->ext_vv_data->flags)) { @@ -453,16 +439,16 @@ static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status) struct saa7146_vv *vv = dev->vv_data; spin_lock(&dev->slock); - if (vv->vbi_q.curr) { - DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_q.curr); + if (vv->vbi_dmaq.curr) { + DEB_VBI("dev:%p, curr:%p\n", dev, vv->vbi_dmaq.curr); /* this must be += 2, one count for each field */ vv->vbi_fieldcount+=2; - vv->vbi_q.curr->vb.field_count = vv->vbi_fieldcount; - saa7146_buffer_finish(dev,&vv->vbi_q,VIDEOBUF_DONE); + vv->vbi_dmaq.curr->vb.field_count = vv->vbi_fieldcount; + saa7146_buffer_finish(dev, &vv->vbi_dmaq, VIDEOBUF_DONE); } else { DEB_VBI("dev:%p\n", dev); } - saa7146_buffer_next(dev,&vv->vbi_q,1); + saa7146_buffer_next(dev, &vv->vbi_dmaq, 1); spin_unlock(&dev->slock); } @@ -488,7 +474,7 @@ static ssize_t vbi_read(struct file *file, char __user *data, size_t count, loff return -EBUSY; } - mod_timer(&fh->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->vbi_read_timeout, jiffies+BUFFER_TIMEOUT); ret = videobuf_read_stream(&fh->vbi_q, data, count, ppos, 1, file->f_flags & O_NONBLOCK); /* diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c index ce30533fd972..6d14785d4747 100644 --- a/drivers/media/common/saa7146_video.c +++ b/drivers/media/common/saa7146_video.c @@ -2,6 +2,8 @@ #include #include +#include +#include #include static int max_memory = 32; @@ -112,8 +114,8 @@ int saa7146_start_preview(struct saa7146_fh *fh) DEB_EE("dev:%p, fh:%p\n", dev, fh); - /* check if we have overlay informations */ - if( NULL == fh->ov.fh ) { + /* check if we have overlay information */ + if (vv->ov.fh == NULL) { DEB_D("no overlay data available. try S_FMT first.\n"); return -EAGAIN; } @@ -139,19 +141,18 @@ int saa7146_start_preview(struct saa7146_fh *fh) return -EBUSY; } - fmt.fmt.win = fh->ov.win; + fmt.fmt.win = vv->ov.win; err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt); if (0 != err) { saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP); return -EBUSY; } - fh->ov.win = fmt.fmt.win; - vv->ov_data = &fh->ov; + vv->ov.win = fmt.fmt.win; DEB_D("%dx%d+%d+%d %s field=%s\n", - fh->ov.win.w.width, fh->ov.win.w.height, - fh->ov.win.w.left, fh->ov.win.w.top, - vv->ov_fmt->name, v4l2_field_names[fh->ov.win.field]); + vv->ov.win.w.width, vv->ov.win.w.height, + vv->ov.win.w.left, vv->ov.win.w.top, + vv->ov_fmt->name, v4l2_field_names[vv->ov.win.field]); if (0 != (ret = saa7146_enable_overlay(fh))) { DEB_D("enabling overlay failed: %d\n", ret); @@ -201,65 +202,6 @@ int saa7146_stop_preview(struct saa7146_fh *fh) } EXPORT_SYMBOL_GPL(saa7146_stop_preview); -/********************************************************************************/ -/* device controls */ - -static struct v4l2_queryctrl controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_CONTRAST, - .name = "Contrast", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_SATURATION, - .name = "Saturation", - .minimum = 0, - .maximum = 127, - .step = 1, - .default_value = 64, - .type = V4L2_CTRL_TYPE_INTEGER, - .flags = V4L2_CTRL_FLAG_SLIDER, - },{ - .id = V4L2_CID_VFLIP, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - },{ - .id = V4L2_CID_HFLIP, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - }, -}; -static int NUM_CONTROLS = sizeof(controls)/sizeof(struct v4l2_queryctrl); - -#define V4L2_CID_PRIVATE_LASTP1 (V4L2_CID_PRIVATE_BASE + 0) - -static struct v4l2_queryctrl* ctrl_by_id(int id) -{ - int i; - - for (i = 0; i < NUM_CONTROLS; i++) - if (controls[i].id == id) - return controls+i; - return NULL; -} - /********************************************************************************/ /* common pagetable functions */ @@ -413,7 +355,7 @@ static int video_begin(struct saa7146_fh *fh) } } - fmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); /* we need to have a valid format set here */ BUG_ON(NULL == fmt); @@ -465,7 +407,7 @@ static int video_end(struct saa7146_fh *fh, struct file *file) return -EBUSY; } - fmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); + fmt = saa7146_format_by_fourcc(dev, vv->video_fmt.pixelformat); /* we need to have a valid format set here */ BUG_ON(NULL == fmt); @@ -504,18 +446,25 @@ static int video_end(struct saa7146_fh *fh, struct file *file) static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap) { + struct video_device *vdev = video_devdata(file); struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; strcpy((char *)cap->driver, "saa7146 v4l2"); strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card)); sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci)); - cap->version = SAA7146_VERSION_CODE; - cap->capabilities = + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - cap->capabilities |= dev->ext_vv_data->capabilities; + cap->device_caps |= dev->ext_vv_data->capabilities; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + if (vdev->vfl_type == VFL_TYPE_GRABBER) + cap->device_caps &= + ~(V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT); + else + cap->device_caps &= + ~(V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_AUDIO); return 0; } @@ -526,6 +475,7 @@ static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *f *fb = vv->ov_fb; fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING; + fb->flags = V4L2_FBUF_FLAG_PRIMARY; return 0; } @@ -579,135 +529,58 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtd return 0; } -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl) { - const struct v4l2_queryctrl *ctrl; - - if ((c->id < V4L2_CID_BASE || - c->id >= V4L2_CID_LASTP1) && - (c->id < V4L2_CID_PRIVATE_BASE || - c->id >= V4L2_CID_PRIVATE_LASTP1)) - return -EINVAL; - - ctrl = ctrl_by_id(c->id); - if (ctrl == NULL) - return -EINVAL; - - DEB_EE("VIDIOC_QUERYCTRL: id:%d\n", c->id); - *c = *ctrl; - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); struct saa7146_vv *vv = dev->vv_data; - const struct v4l2_queryctrl *ctrl; - u32 value = 0; + u32 val; - ctrl = ctrl_by_id(c->id); - if (NULL == ctrl) - return -EINVAL; - switch (c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0xff & (value >> 24); - DEB_D("V4L2_CID_BRIGHTNESS: %d\n", c->value); + val = saa7146_read(dev, BCS_CTRL); + val &= 0x00ffffff; + val |= (ctrl->val << 24); + saa7146_write(dev, BCS_CTRL, val); + saa7146_write(dev, MC2, MASK_22 | MASK_06); break; + case V4L2_CID_CONTRAST: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0x7f & (value >> 16); - DEB_D("V4L2_CID_CONTRAST: %d\n", c->value); + val = saa7146_read(dev, BCS_CTRL); + val &= 0xff00ffff; + val |= (ctrl->val << 16); + saa7146_write(dev, BCS_CTRL, val); + saa7146_write(dev, MC2, MASK_22 | MASK_06); break; + case V4L2_CID_SATURATION: - value = saa7146_read(dev, BCS_CTRL); - c->value = 0x7f & (value >> 0); - DEB_D("V4L2_CID_SATURATION: %d\n", c->value); - break; - case V4L2_CID_VFLIP: - c->value = vv->vflip; - DEB_D("V4L2_CID_VFLIP: %d\n", c->value); - break; - case V4L2_CID_HFLIP: - c->value = vv->hflip; - DEB_D("V4L2_CID_HFLIP: %d\n", c->value); - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct saa7146_vv *vv = dev->vv_data; - const struct v4l2_queryctrl *ctrl; - - ctrl = ctrl_by_id(c->id); - if (NULL == ctrl) { - DEB_D("unknown control %d\n", c->id); - return -EINVAL; - } - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_INTEGER: - if (c->value < ctrl->minimum) - c->value = ctrl->minimum; - if (c->value > ctrl->maximum) - c->value = ctrl->maximum; - break; - default: - /* nothing */; - } - - switch (c->id) { - case V4L2_CID_BRIGHTNESS: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0x00ffffff; - value |= (c->value << 24); - saa7146_write(dev, BCS_CTRL, value); + val = saa7146_read(dev, BCS_CTRL); + val &= 0xffffff00; + val |= (ctrl->val << 0); + saa7146_write(dev, BCS_CTRL, val); saa7146_write(dev, MC2, MASK_22 | MASK_06); break; - } - case V4L2_CID_CONTRAST: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0xff00ffff; - value |= (c->value << 16); - saa7146_write(dev, BCS_CTRL, value); - saa7146_write(dev, MC2, MASK_22 | MASK_06); - break; - } - case V4L2_CID_SATURATION: { - u32 value = saa7146_read(dev, BCS_CTRL); - value &= 0xffffff00; - value |= (c->value << 0); - saa7146_write(dev, BCS_CTRL, value); - saa7146_write(dev, MC2, MASK_22 | MASK_06); - break; - } + case V4L2_CID_HFLIP: /* fixme: we can support changing VFLIP and HFLIP here... */ - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_D("V4L2_CID_HFLIP while active capture\n"); + if ((vv->video_status & STATUS_CAPTURE)) return -EBUSY; - } - vv->hflip = c->value; + vv->hflip = ctrl->val; break; + case V4L2_CID_VFLIP: - if (IS_CAPTURE_ACTIVE(fh) != 0) { - DEB_D("V4L2_CID_VFLIP while active capture\n"); + if ((vv->video_status & STATUS_CAPTURE)) return -EBUSY; - } - vv->vflip = c->value; + vv->vflip = ctrl->val; break; + default: return -EINVAL; } - if (IS_OVERLAY_ACTIVE(fh) != 0) { + if ((vv->video_status & STATUS_OVERLAY) != 0) { /* CHECK: && (vv->video_fh == fh)) */ + struct saa7146_fh *fh = vv->video_fh; + saa7146_stop_preview(fh); saa7146_start_preview(fh); } @@ -720,6 +593,8 @@ static int vidioc_g_parm(struct file *file, void *fh, struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct saa7146_vv *vv = dev->vv_data; + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; parm->parm.capture.readbuffers = 1; v4l2_video_std_frame_period(vv->standard->id, &parm->parm.capture.timeperframe); @@ -728,19 +603,28 @@ static int vidioc_g_parm(struct file *file, void *fh, static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.pix = ((struct saa7146_fh *)fh)->video_fmt; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.pix = vv->video_fmt; return 0; } static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.win = ((struct saa7146_fh *)fh)->ov.win; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.win = vv->ov.win; return 0; } static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f) { - f->fmt.vbi = ((struct saa7146_fh *)fh)->vbi_fmt; + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct saa7146_vv *vv = dev->vv_data; + + f->fmt.vbi = vv->vbi_fmt; return 0; } @@ -787,6 +671,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_forma } f->fmt.pix.field = field; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; if (f->fmt.pix.width > maxw) f->fmt.pix.width = maxw; if (f->fmt.pix.height > maxh) @@ -883,9 +768,9 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_forma err = vidioc_try_fmt_vid_cap(file, fh, f); if (0 != err) return err; - fh->video_fmt = f->fmt.pix; + vv->video_fmt = f->fmt.pix; DEB_EE("set to pixelformat '%4.4s'\n", - (char *)&fh->video_fmt.pixelformat); + (char *)&vv->video_fmt.pixelformat); return 0; } @@ -900,17 +785,17 @@ static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_f err = vidioc_try_fmt_vid_overlay(file, fh, f); if (0 != err) return err; - fh->ov.win = f->fmt.win; - fh->ov.nclips = f->fmt.win.clipcount; - if (fh->ov.nclips > 16) - fh->ov.nclips = 16; - if (copy_from_user(fh->ov.clips, f->fmt.win.clips, - sizeof(struct v4l2_clip) * fh->ov.nclips)) { + vv->ov.win = f->fmt.win; + vv->ov.nclips = f->fmt.win.clipcount; + if (vv->ov.nclips > 16) + vv->ov.nclips = 16; + if (copy_from_user(vv->ov.clips, f->fmt.win.clips, + sizeof(struct v4l2_clip) * vv->ov.nclips)) { return -EFAULT; } - /* fh->ov.fh is used to indicate that we have valid overlay informations, too */ - fh->ov.fh = fh; + /* vv->ov.fh is used to indicate that we have valid overlay informations, too */ + vv->ov.fh = fh; /* check if our current overlay is active */ if (IS_OVERLAY_ACTIVE(fh) != 0) { @@ -1111,10 +996,14 @@ static int vidioc_g_chip_ident(struct file *file, void *__fh, chip->ident = V4L2_IDENT_NONE; chip->revision = 0; - if (chip->match.type == V4L2_CHIP_MATCH_HOST && !chip->match.addr) { - chip->ident = V4L2_IDENT_SAA7146; + if (chip->match.type == V4L2_CHIP_MATCH_HOST) { + if (v4l2_chip_match_host(&chip->match)) + chip->ident = V4L2_IDENT_SAA7146; return 0; } + if (chip->match.type != V4L2_CHIP_MATCH_I2C_DRIVER && + chip->match.type != V4L2_CHIP_MATCH_I2C_ADDR) + return -EINVAL; return v4l2_device_call_until_err(&dev->v4l2_dev, 0, core, g_chip_ident, chip); } @@ -1129,7 +1018,6 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay, .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay, .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay, - .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, .vidioc_g_chip_ident = vidioc_g_chip_ident, .vidioc_overlay = vidioc_overlay, @@ -1141,12 +1029,29 @@ const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = { .vidioc_dqbuf = vidioc_dqbuf, .vidioc_g_std = vidioc_g_std, .vidioc_s_std = vidioc_s_std, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_streamon = vidioc_streamon, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, + .vidioc_g_chip_ident = vidioc_g_chip_ident, + + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /*********************************************************************************/ @@ -1161,7 +1066,7 @@ static int buffer_activate (struct saa7146_dev *dev, buf->vb.state = VIDEOBUF_ACTIVE; saa7146_set_capture(dev,buf,next); - mod_timer(&vv->video_q.timeout, jiffies+BUFFER_TIMEOUT); + mod_timer(&vv->video_dmaq.timeout, jiffies+BUFFER_TIMEOUT); return 0; } @@ -1185,44 +1090,44 @@ static int buffer_prepare(struct videobuf_queue *q, DEB_CAP("vbuf:%p\n", vb); /* sanity checks */ - if (fh->video_fmt.width < 48 || - fh->video_fmt.height < 32 || - fh->video_fmt.width > vv->standard->h_max_out || - fh->video_fmt.height > vv->standard->v_max_out) { + if (vv->video_fmt.width < 48 || + vv->video_fmt.height < 32 || + vv->video_fmt.width > vv->standard->h_max_out || + vv->video_fmt.height > vv->standard->v_max_out) { DEB_D("w (%d) / h (%d) out of bounds\n", - fh->video_fmt.width, fh->video_fmt.height); + vv->video_fmt.width, vv->video_fmt.height); return -EINVAL; } - size = fh->video_fmt.sizeimage; + size = vv->video_fmt.sizeimage; if (0 != buf->vb.baddr && buf->vb.bsize < size) { DEB_D("size mismatch\n"); return -EINVAL; } DEB_CAP("buffer_prepare [size=%dx%d,bytes=%d,fields=%s]\n", - fh->video_fmt.width, fh->video_fmt.height, - size, v4l2_field_names[fh->video_fmt.field]); - if (buf->vb.width != fh->video_fmt.width || - buf->vb.bytesperline != fh->video_fmt.bytesperline || - buf->vb.height != fh->video_fmt.height || + vv->video_fmt.width, vv->video_fmt.height, + size, v4l2_field_names[vv->video_fmt.field]); + if (buf->vb.width != vv->video_fmt.width || + buf->vb.bytesperline != vv->video_fmt.bytesperline || + buf->vb.height != vv->video_fmt.height || buf->vb.size != size || buf->vb.field != field || - buf->vb.field != fh->video_fmt.field || - buf->fmt != &fh->video_fmt) { + buf->vb.field != vv->video_fmt.field || + buf->fmt != &vv->video_fmt) { saa7146_dma_free(dev,q,buf); } if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { struct saa7146_format *sfmt; - buf->vb.bytesperline = fh->video_fmt.bytesperline; - buf->vb.width = fh->video_fmt.width; - buf->vb.height = fh->video_fmt.height; + buf->vb.bytesperline = vv->video_fmt.bytesperline; + buf->vb.width = vv->video_fmt.width; + buf->vb.height = vv->video_fmt.height; buf->vb.size = size; buf->vb.field = field; - buf->fmt = &fh->video_fmt; - buf->vb.field = fh->video_fmt.field; + buf->fmt = &vv->video_fmt; + buf->vb.field = vv->video_fmt.field; sfmt = saa7146_format_by_fourcc(dev,buf->fmt->pixelformat); @@ -1258,11 +1163,12 @@ static int buffer_setup(struct videobuf_queue *q, unsigned int *count, unsigned { struct file *file = q->priv_data; struct saa7146_fh *fh = file->private_data; + struct saa7146_vv *vv = fh->dev->vv_data; if (0 == *count || *count > MAX_SAA7146_CAPTURE_BUFFERS) *count = MAX_SAA7146_CAPTURE_BUFFERS; - *size = fh->video_fmt.sizeimage; + *size = vv->video_fmt.sizeimage; /* check if we exceed the "max_memory" parameter */ if( (*count * *size) > (max_memory*1048576) ) { @@ -1283,7 +1189,7 @@ static void buffer_queue(struct videobuf_queue *q, struct videobuf_buffer *vb) struct saa7146_buf *buf = (struct saa7146_buf *)vb; DEB_CAP("vbuf:%p\n", vb); - saa7146_buffer_queue(fh->dev,&vv->video_q,buf); + saa7146_buffer_queue(fh->dev, &vv->video_dmaq, buf); } static void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) @@ -1312,12 +1218,12 @@ static struct videobuf_queue_ops video_qops = { static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) { - INIT_LIST_HEAD(&vv->video_q.queue); + INIT_LIST_HEAD(&vv->video_dmaq.queue); - init_timer(&vv->video_q.timeout); - vv->video_q.timeout.function = saa7146_buffer_timeout; - vv->video_q.timeout.data = (unsigned long)(&vv->video_q); - vv->video_q.dev = dev; + init_timer(&vv->video_dmaq.timeout); + vv->video_dmaq.timeout.function = saa7146_buffer_timeout; + vv->video_dmaq.timeout.data = (unsigned long)(&vv->video_dmaq); + vv->video_dmaq.dev = dev; /* set some default values */ vv->standard = &dev->ext_vv_data->stds[0]; @@ -1331,15 +1237,6 @@ static void video_init(struct saa7146_dev *dev, struct saa7146_vv *vv) static int video_open(struct saa7146_dev *dev, struct file *file) { struct saa7146_fh *fh = file->private_data; - struct saa7146_format *sfmt; - - fh->video_fmt.width = 384; - fh->video_fmt.height = 288; - fh->video_fmt.pixelformat = V4L2_PIX_FMT_BGR24; - fh->video_fmt.bytesperline = 0; - fh->video_fmt.field = V4L2_FIELD_ANY; - sfmt = saa7146_format_by_fourcc(dev,fh->video_fmt.pixelformat); - fh->video_fmt.sizeimage = (fh->video_fmt.width * fh->video_fmt.height * sfmt->depth)/8; videobuf_queue_sg_init(&fh->video_q, &video_qops, &dev->pci->dev, &dev->slock, @@ -1371,7 +1268,7 @@ static void video_close(struct saa7146_dev *dev, struct file *file) static void video_irq_done(struct saa7146_dev *dev, unsigned long st) { struct saa7146_vv *vv = dev->vv_data; - struct saa7146_dmaqueue *q = &vv->video_q; + struct saa7146_dmaqueue *q = &vv->video_dmaq; spin_lock(&dev->slock); DEB_CAP("called\n"); diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig index 4a6d5cef3964..bbf4945149a9 100644 --- a/drivers/media/common/tuners/Kconfig +++ b/drivers/media/common/tuners/Kconfig @@ -204,6 +204,27 @@ config MEDIA_TUNER_TDA18218 help NXP TDA18218 silicon tuner driver. +config MEDIA_TUNER_FC0011 + tristate "Fitipower FC0011 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0011 silicon tuner driver. + +config MEDIA_TUNER_FC0012 + tristate "Fitipower FC0012 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0012 silicon tuner driver. + +config MEDIA_TUNER_FC0013 + tristate "Fitipower FC0013 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Fitipower FC0013 silicon tuner driver. + config MEDIA_TUNER_TDA18212 tristate "NXP TDA18212 silicon tuner" depends on VIDEO_MEDIA && I2C @@ -211,4 +232,10 @@ config MEDIA_TUNER_TDA18212 help NXP TDA18212 silicon tuner driver. +config MEDIA_TUNER_TUA9001 + tristate "Infineon TUA 9001 silicon tuner" + depends on VIDEO_MEDIA && I2C + default m if MEDIA_TUNER_CUSTOMISE + help + Infineon TUA 9001 silicon tuner driver. endmenu diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile index f80407eb8998..891b80e60808 100644 --- a/drivers/media/common/tuners/Makefile +++ b/drivers/media/common/tuners/Makefile @@ -28,6 +28,10 @@ obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o obj-$(CONFIG_MEDIA_TUNER_MAX2165) += max2165.o obj-$(CONFIG_MEDIA_TUNER_TDA18218) += tda18218.o obj-$(CONFIG_MEDIA_TUNER_TDA18212) += tda18212.o +obj-$(CONFIG_MEDIA_TUNER_TUA9001) += tua9001.o +obj-$(CONFIG_MEDIA_TUNER_FC0011) += fc0011.o +obj-$(CONFIG_MEDIA_TUNER_FC0012) += fc0012.o +obj-$(CONFIG_MEDIA_TUNER_FC0013) += fc0013.o ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends diff --git a/drivers/media/common/tuners/fc0011.c b/drivers/media/common/tuners/fc0011.c new file mode 100644 index 000000000000..e4882546c283 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.c @@ -0,0 +1,524 @@ +/* + * Fitipower FC0011 tuner driver + * + * Copyright (C) 2012 Michael Buesch + * + * Derived from FC0012 tuner driver: + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 "fc0011.h" + + +/* Tuner registers */ +enum { + FC11_REG_0, + FC11_REG_FA, /* FA */ + FC11_REG_FP, /* FP */ + FC11_REG_XINHI, /* XIN high 8 bit */ + FC11_REG_XINLO, /* XIN low 8 bit */ + FC11_REG_VCO, /* VCO */ + FC11_REG_VCOSEL, /* VCO select */ + FC11_REG_7, /* Unknown tuner reg 7 */ + FC11_REG_8, /* Unknown tuner reg 8 */ + FC11_REG_9, + FC11_REG_10, /* Unknown tuner reg 10 */ + FC11_REG_11, /* Unknown tuner reg 11 */ + FC11_REG_12, + FC11_REG_RCCAL, /* RC calibrate */ + FC11_REG_VCOCAL, /* VCO calibrate */ + FC11_REG_15, + FC11_REG_16, /* Unknown tuner reg 16 */ + FC11_REG_17, + + FC11_NR_REGS, /* Number of registers */ +}; + +enum FC11_REG_VCOSEL_bits { + FC11_VCOSEL_2 = 0x08, /* VCO select 2 */ + FC11_VCOSEL_1 = 0x10, /* VCO select 1 */ + FC11_VCOSEL_CLKOUT = 0x20, /* Fix clock out */ + FC11_VCOSEL_BW7M = 0x40, /* 7MHz bw */ + FC11_VCOSEL_BW6M = 0x80, /* 6MHz bw */ +}; + +enum FC11_REG_RCCAL_bits { + FC11_RCCAL_FORCE = 0x10, /* force */ +}; + +enum FC11_REG_VCOCAL_bits { + FC11_VCOCAL_RUN = 0, /* VCO calibration run */ + FC11_VCOCAL_VALUEMASK = 0x3F, /* VCO calibration value mask */ + FC11_VCOCAL_OK = 0x40, /* VCO calibration Ok */ + FC11_VCOCAL_RESET = 0x80, /* VCO calibration reset */ +}; + + +struct fc0011_priv { + struct i2c_adapter *i2c; + u8 addr; + + u32 frequency; + u32 bandwidth; +}; + + +static int fc0011_writereg(struct fc0011_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = { reg, val }; + struct i2c_msg msg = { .addr = priv->addr, + .flags = 0, .buf = buf, .len = 2 }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + dev_err(&priv->i2c->dev, + "I2C write reg failed, reg: %02x, val: %02x\n", + reg, val); + return -EIO; + } + + return 0; +} + +static int fc0011_readreg(struct fc0011_priv *priv, u8 reg, u8 *val) +{ + u8 dummy; + struct i2c_msg msg[2] = { + { .addr = priv->addr, + .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, + .flags = I2C_M_RD, .buf = val ? : &dummy, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + dev_err(&priv->i2c->dev, + "I2C read failed, reg: %02x\n", reg); + return -EIO; + } + + return 0; +} + +static int fc0011_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int fc0011_init(struct dvb_frontend *fe) +{ + struct fc0011_priv *priv = fe->tuner_priv; + int err; + + if (WARN_ON(!fe->callback)) + return -EINVAL; + + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_POWER, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Power-on callback failed\n"); + return err; + } + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Reset callback failed\n"); + return err; + } + + return 0; +} + +/* Initiate VCO calibration */ +static int fc0011_vcocal_trigger(struct fc0011_priv *priv) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RESET); + if (err) + return err; + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + + return 0; +} + +/* Read VCO calibration value */ +static int fc0011_vcocal_read(struct fc0011_priv *priv, u8 *value) +{ + int err; + + err = fc0011_writereg(priv, FC11_REG_VCOCAL, FC11_VCOCAL_RUN); + if (err) + return err; + usleep_range(10000, 20000); + err = fc0011_readreg(priv, FC11_REG_VCOCAL, value); + if (err) + return err; + + return 0; +} + +static int fc0011_set_params(struct dvb_frontend *fe) +{ + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + struct fc0011_priv *priv = fe->tuner_priv; + int err; + unsigned int i, vco_retries; + u32 freq = p->frequency / 1000; + u32 bandwidth = p->bandwidth_hz / 1000; + u32 fvco, xin, xdiv, xdivr; + u16 frac; + u8 fa, fp, vco_sel, vco_cal; + u8 regs[FC11_NR_REGS] = { }; + + regs[FC11_REG_7] = 0x0F; + regs[FC11_REG_8] = 0x3E; + regs[FC11_REG_10] = 0xB8; + regs[FC11_REG_11] = 0x80; + regs[FC11_REG_RCCAL] = 0x04; + err = fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); + err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); + err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); + err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); + err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return -EIO; + + /* Set VCO freq and VCO div */ + if (freq < 54000) { + fvco = freq * 64; + regs[FC11_REG_VCO] = 0x82; + } else if (freq < 108000) { + fvco = freq * 32; + regs[FC11_REG_VCO] = 0x42; + } else if (freq < 216000) { + fvco = freq * 16; + regs[FC11_REG_VCO] = 0x22; + } else if (freq < 432000) { + fvco = freq * 8; + regs[FC11_REG_VCO] = 0x12; + } else { + fvco = freq * 4; + regs[FC11_REG_VCO] = 0x0A; + } + + /* Calc XIN. The PLL reference frequency is 18 MHz. */ + xdiv = fvco / 18000; + frac = fvco - xdiv * 18000; + frac = (frac << 15) / 18000; + if (frac >= 16384) + frac += 32786; + if (!frac) + xin = 0; + else if (frac < 511) + xin = 512; + else if (frac < 65026) + xin = frac; + else + xin = 65024; + regs[FC11_REG_XINHI] = xin >> 8; + regs[FC11_REG_XINLO] = xin; + + /* Calc FP and FA */ + xdivr = xdiv; + if (fvco - xdiv * 18000 >= 9000) + xdivr += 1; /* round */ + fp = xdivr / 8; + fa = xdivr - fp * 8; + if (fa < 2) { + fp -= 1; + fa += 8; + } + if (fp > 0x1F) { + fp &= 0x1F; + fa &= 0xF; + } + if (fa >= fp) { + dev_warn(&priv->i2c->dev, + "fa %02X >= fp %02X, but trying to continue\n", + (unsigned int)(u8)fa, (unsigned int)(u8)fp); + } + regs[FC11_REG_FA] = fa; + regs[FC11_REG_FP] = fp; + + /* Select bandwidth */ + switch (bandwidth) { + case 8000: + break; + case 7000: + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW7M; + break; + default: + dev_warn(&priv->i2c->dev, "Unsupported bandwidth %u kHz. " + "Using 6000 kHz.\n", + bandwidth); + bandwidth = 6000; + /* fallthrough */ + case 6000: + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_BW6M; + break; + } + + /* Pre VCO select */ + if (fvco < 2320000) { + vco_sel = 0; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + } else if (fvco < 3080000) { + vco_sel = 1; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + } else { + vco_sel = 2; + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + } + + /* Fix for low freqs */ + if (freq < 45000) { + regs[FC11_REG_FA] = 0x6; + regs[FC11_REG_FP] = 0x11; + } + + /* Clock out fix */ + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_CLKOUT; + + /* Write the cached registers */ + for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) { + err = fc0011_writereg(priv, i, regs[i]); + if (err) + return err; + } + + /* VCO calibration */ + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries = 0; + while (!(vco_cal & FC11_VCOCAL_OK) && vco_retries < 3) { + /* Reset the tuner and try again */ + err = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC0011_FE_CALLBACK_RESET, priv->addr); + if (err) { + dev_err(&priv->i2c->dev, "Failed to reset tuner\n"); + return err; + } + /* Reinit tuner config */ + err = 0; + for (i = FC11_REG_FA; i <= FC11_REG_VCOSEL; i++) + err |= fc0011_writereg(priv, i, regs[i]); + err |= fc0011_writereg(priv, FC11_REG_7, regs[FC11_REG_7]); + err |= fc0011_writereg(priv, FC11_REG_8, regs[FC11_REG_8]); + err |= fc0011_writereg(priv, FC11_REG_10, regs[FC11_REG_10]); + err |= fc0011_writereg(priv, FC11_REG_11, regs[FC11_REG_11]); + err |= fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return -EIO; + /* VCO calibration */ + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + err = fc0011_vcocal_read(priv, &vco_cal); + if (err) + return err; + vco_retries++; + } + if (!(vco_cal & FC11_VCOCAL_OK)) { + dev_err(&priv->i2c->dev, + "Failed to read VCO calibration value (got %02X)\n", + (unsigned int)vco_cal); + return -EIO; + } + vco_cal &= FC11_VCOCAL_VALUEMASK; + + switch (vco_sel) { + case 0: + if (vco_cal < 8) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } + break; + case 1: + if (vco_cal < 5) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else if (vco_cal <= 48) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } + break; + case 2: + if (vco_cal > 53) { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_1; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + err = fc0011_vcocal_trigger(priv); + if (err) + return err; + } else { + regs[FC11_REG_VCOSEL] &= ~(FC11_VCOSEL_1 | FC11_VCOSEL_2); + regs[FC11_REG_VCOSEL] |= FC11_VCOSEL_2; + err = fc0011_writereg(priv, FC11_REG_VCOSEL, + regs[FC11_REG_VCOSEL]); + if (err) + return err; + } + break; + } + err = fc0011_vcocal_read(priv, NULL); + if (err) + return err; + usleep_range(10000, 50000); + + err = fc0011_readreg(priv, FC11_REG_RCCAL, ®s[FC11_REG_RCCAL]); + if (err) + return err; + regs[FC11_REG_RCCAL] |= FC11_RCCAL_FORCE; + err = fc0011_writereg(priv, FC11_REG_RCCAL, regs[FC11_REG_RCCAL]); + if (err) + return err; + err = fc0011_writereg(priv, FC11_REG_16, 0xB); + if (err) + return err; + + dev_dbg(&priv->i2c->dev, "Tuned to " + "fa=%02X fp=%02X xin=%02X%02X vco=%02X vcosel=%02X " + "vcocal=%02X(%u) bw=%u\n", + (unsigned int)regs[FC11_REG_FA], + (unsigned int)regs[FC11_REG_FP], + (unsigned int)regs[FC11_REG_XINHI], + (unsigned int)regs[FC11_REG_XINLO], + (unsigned int)regs[FC11_REG_VCO], + (unsigned int)regs[FC11_REG_VCOSEL], + (unsigned int)vco_cal, vco_retries, + (unsigned int)bandwidth); + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + + return 0; +} + +static int fc0011_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0011_priv *priv = fe->tuner_priv; + + *frequency = priv->frequency; + + return 0; +} + +static int fc0011_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; + + return 0; +} + +static int fc0011_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0011_priv *priv = fe->tuner_priv; + + *bandwidth = priv->bandwidth; + + return 0; +} + +static const struct dvb_tuner_ops fc0011_tuner_ops = { + .info = { + .name = "Fitipower FC0011", + + .frequency_min = 45000000, + .frequency_max = 1000000000, + }, + + .release = fc0011_release, + .init = fc0011_init, + + .set_params = fc0011_set_params, + + .get_frequency = fc0011_get_frequency, + .get_if_frequency = fc0011_get_if_frequency, + .get_bandwidth = fc0011_get_bandwidth, +}; + +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config) +{ + struct fc0011_priv *priv; + + priv = kzalloc(sizeof(struct fc0011_priv), GFP_KERNEL); + if (!priv) + return NULL; + + priv->i2c = i2c; + priv->addr = config->i2c_address; + + fe->tuner_priv = priv; + fe->ops.tuner_ops = fc0011_tuner_ops; + + dev_info(&priv->i2c->dev, "Fitipower FC0011 tuner attached\n"); + + return fe; +} +EXPORT_SYMBOL(fc0011_attach); + +MODULE_DESCRIPTION("Fitipower FC0011 silicon tuner driver"); +MODULE_AUTHOR("Michael Buesch "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/fc0011.h b/drivers/media/common/tuners/fc0011.h new file mode 100644 index 000000000000..0ee581f122d2 --- /dev/null +++ b/drivers/media/common/tuners/fc0011.h @@ -0,0 +1,41 @@ +#ifndef LINUX_FC0011_H_ +#define LINUX_FC0011_H_ + +#include "dvb_frontend.h" + + +/** struct fc0011_config - fc0011 hardware config + * + * @i2c_address: I2C bus address. + */ +struct fc0011_config { + u8 i2c_address; +}; + +/** enum fc0011_fe_callback_commands - Frontend callbacks + * + * @FC0011_FE_CALLBACK_POWER: Power on tuner hardware. + * @FC0011_FE_CALLBACK_RESET: Request a tuner reset. + */ +enum fc0011_fe_callback_commands { + FC0011_FE_CALLBACK_POWER, + FC0011_FE_CALLBACK_RESET, +}; + +#if defined(CONFIG_MEDIA_TUNER_FC0011) ||\ + defined(CONFIG_MEDIA_TUNER_FC0011_MODULE) +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config); +#else +static inline +struct dvb_frontend *fc0011_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + const struct fc0011_config *config) +{ + dev_err(&i2c->dev, "fc0011 driver disabled in Kconfig\n"); + return NULL; +} +#endif + +#endif /* LINUX_FC0011_H_ */ diff --git a/drivers/media/common/tuners/fc0012-priv.h b/drivers/media/common/tuners/fc0012-priv.h new file mode 100644 index 000000000000..4577c917e616 --- /dev/null +++ b/drivers/media/common/tuners/fc0012-priv.h @@ -0,0 +1,43 @@ +/* + * Fitipower FC0012 tuner driver - private includes + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 _FC0012_PRIV_H_ +#define _FC0012_PRIV_H_ + +#define LOG_PREFIX "fc0012" + +#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) + +struct fc0012_priv { + struct i2c_adapter *i2c; + u8 addr; + u8 dual_master; + u8 xtal_freq; + + u32 frequency; + u32 bandwidth; +}; + +#endif diff --git a/drivers/media/common/tuners/fc0012.c b/drivers/media/common/tuners/fc0012.c new file mode 100644 index 000000000000..308135abd54c --- /dev/null +++ b/drivers/media/common/tuners/fc0012.c @@ -0,0 +1,467 @@ +/* + * Fitipower FC0012 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 "fc0012.h" +#include "fc0012-priv.h" + +static int fc0012_writereg(struct fc0012_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + return -EREMOTEIO; + } + return 0; +} + +static int fc0012_readreg(struct fc0012_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + err("I2C read reg failed, reg: %02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int fc0012_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int fc0012_init(struct dvb_frontend *fe) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int i, ret = 0; + unsigned char reg[] = { + 0x00, /* dummy reg. 0 */ + 0x05, /* reg. 0x01 */ + 0x10, /* reg. 0x02 */ + 0x00, /* reg. 0x03 */ + 0x00, /* reg. 0x04 */ + 0x0f, /* reg. 0x05: may also be 0x0a */ + 0x00, /* reg. 0x06: divider 2, VCO slow */ + 0x00, /* reg. 0x07: may also be 0x0f */ + 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, + Loop Bw 1/8 */ + 0x6e, /* reg. 0x09: Disable LoopThrough, Enable LoopThrough: 0x6f */ + 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ + 0x82, /* reg. 0x0b: Output Clock is same as clock frequency, + may also be 0x83 */ + 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ + 0x02, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, 0x02 for DVB-T */ + 0x00, /* reg. 0x0e */ + 0x00, /* reg. 0x0f */ + 0x00, /* reg. 0x10: may also be 0x0d */ + 0x00, /* reg. 0x11 */ + 0x1f, /* reg. 0x12: Set to maximum gain */ + 0x08, /* reg. 0x13: Set to Middle Gain: 0x08, + Low Gain: 0x00, High Gain: 0x10, enable IX2: 0x80 */ + 0x00, /* reg. 0x14 */ + 0x04, /* reg. 0x15: Enable LNA COMPS */ + }; + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + case FC_XTAL_28_8_MHZ: + reg[0x07] |= 0x20; + break; + case FC_XTAL_36_MHZ: + default: + break; + } + + if (priv->dual_master) + reg[0x0c] |= 0x02; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i < sizeof(reg); i++) { + ret = fc0012_writereg(priv, i, reg[i]); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + err("fc0012_writereg failed: %d", ret); + + return ret; +} + +static int fc0012_sleep(struct dvb_frontend *fe) +{ + /* nothing to do here */ + return 0; +} + +static int fc0012_set_params(struct dvb_frontend *fe) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int i, ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq = p->frequency / 1000; + u32 delsys = p->delivery_system; + unsigned char reg[7], am, pm, multi, tmp; + unsigned long f_vco; + unsigned short xtal_freq_khz_2, xin, xdiv; + int vco_select = false; + + if (fe->callback) { + ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); + if (ret) + goto exit; + } + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + xtal_freq_khz_2 = 27000 / 2; + break; + case FC_XTAL_36_MHZ: + xtal_freq_khz_2 = 36000 / 2; + break; + case FC_XTAL_28_8_MHZ: + default: + xtal_freq_khz_2 = 28800 / 2; + break; + } + + /* select frequency divider and the frequency of VCO */ + if (freq < 37084) { /* freq * 96 < 3560000 */ + multi = 96; + reg[5] = 0x82; + reg[6] = 0x00; + } else if (freq < 55625) { /* freq * 64 < 3560000 */ + multi = 64; + reg[5] = 0x82; + reg[6] = 0x02; + } else if (freq < 74167) { /* freq * 48 < 3560000 */ + multi = 48; + reg[5] = 0x42; + reg[6] = 0x00; + } else if (freq < 111250) { /* freq * 32 < 3560000 */ + multi = 32; + reg[5] = 0x42; + reg[6] = 0x02; + } else if (freq < 148334) { /* freq * 24 < 3560000 */ + multi = 24; + reg[5] = 0x22; + reg[6] = 0x00; + } else if (freq < 222500) { /* freq * 16 < 3560000 */ + multi = 16; + reg[5] = 0x22; + reg[6] = 0x02; + } else if (freq < 296667) { /* freq * 12 < 3560000 */ + multi = 12; + reg[5] = 0x12; + reg[6] = 0x00; + } else if (freq < 445000) { /* freq * 8 < 3560000 */ + multi = 8; + reg[5] = 0x12; + reg[6] = 0x02; + } else if (freq < 593334) { /* freq * 6 < 3560000 */ + multi = 6; + reg[5] = 0x0a; + reg[6] = 0x00; + } else { + multi = 4; + reg[5] = 0x0a; + reg[6] = 0x02; + } + + f_vco = freq * multi; + + if (f_vco >= 3060000) { + reg[6] |= 0x08; + vco_select = true; + } + + if (freq >= 45000) { + /* From divided value (XDIV) determined the FA and FP value */ + xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); + if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) + xdiv++; + + pm = (unsigned char)(xdiv / 8); + am = (unsigned char)(xdiv - (8 * pm)); + + if (am < 2) { + reg[1] = am + 8; + reg[2] = pm - 1; + } else { + reg[1] = am; + reg[2] = pm; + } + } else { + /* fix for frequency less than 45 MHz */ + reg[1] = 0x06; + reg[2] = 0x11; + } + + /* fix clock out */ + reg[6] |= 0x20; + + /* From VCO frequency determines the XIN ( fractional part of Delta + Sigma PLL) and divided value (XDIV) */ + xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); + xin = (xin << 15) / xtal_freq_khz_2; + if (xin >= 16384) + xin += 32768; + + reg[3] = xin >> 8; /* xin with 9 bit resolution */ + reg[4] = xin & 0xff; + + if (delsys == SYS_DVBT) { + reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ + switch (p->bandwidth_hz) { + case 6000000: + reg[6] |= 0x80; + break; + case 7000000: + reg[6] |= 0x40; + break; + case 8000000: + default: + break; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + + /* modified for Realtek demod */ + reg[5] |= 0x07; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i <= 6; i++) { + ret = fc0012_writereg(priv, i, reg[i]); + if (ret) + goto exit; + } + + /* VCO Calibration */ + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + + /* VCO Re-Calibration if needed */ + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + + if (!ret) { + msleep(10); + ret = fc0012_readreg(priv, 0x0e, &tmp); + } + if (ret) + goto exit; + + /* vco selection */ + tmp &= 0x3f; + + if (vco_select) { + if (tmp > 0x3c) { + reg[6] &= ~0x08; + ret = fc0012_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + } + } else { + if (tmp < 0x02) { + reg[6] |= 0x08; + ret = fc0012_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0012_writereg(priv, 0x0e, 0x00); + } + } + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static int fc0012_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0012_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int fc0012_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + /* CHECK: always ? */ + *frequency = 0; + return 0; +} + +static int fc0012_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0012_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +#define INPUT_ADC_LEVEL -8 + +static int fc0012_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct fc0012_priv *priv = fe->tuner_priv; + int ret; + unsigned char tmp; + int int_temp, lna_gain, int_lna, tot_agc_gain, power; + const int fc0012_lna_gain_table[] = { + /* low gain */ + -63, -58, -99, -73, + -63, -65, -54, -60, + /* middle gain */ + 71, 70, 68, 67, + 65, 63, 61, 58, + /* high gain */ + 197, 191, 188, 186, + 184, 182, 181, 179, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0012_writereg(priv, 0x12, 0x00); + if (ret) + goto err; + + ret = fc0012_readreg(priv, 0x12, &tmp); + if (ret) + goto err; + int_temp = tmp; + + ret = fc0012_readreg(priv, 0x13, &tmp); + if (ret) + goto err; + lna_gain = tmp & 0x1f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (lna_gain < ARRAY_SIZE(fc0012_lna_gain_table)) { + int_lna = fc0012_lna_gain_table[lna_gain]; + tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + + (int_temp & 0x1f)) * 2; + power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; + + if (power >= 45) + *strength = 255; /* 100% */ + else if (power < -95) + *strength = 0; + else + *strength = (power + 95) * 255 / 140; + + *strength |= *strength << 8; + } else { + ret = -1; + } + + goto exit; + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +exit: + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static const struct dvb_tuner_ops fc0012_tuner_ops = { + .info = { + .name = "Fitipower FC0012", + + .frequency_min = 37000000, /* estimate */ + .frequency_max = 862000000, /* estimate */ + .frequency_step = 0, + }, + + .release = fc0012_release, + + .init = fc0012_init, + .sleep = fc0012_sleep, + + .set_params = fc0012_set_params, + + .get_frequency = fc0012_get_frequency, + .get_if_frequency = fc0012_get_if_frequency, + .get_bandwidth = fc0012_get_bandwidth, + + .get_rf_strength = fc0012_get_rf_strength, +}; + +struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + struct fc0012_priv *priv = NULL; + + priv = kzalloc(sizeof(struct fc0012_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + priv->dual_master = dual_master; + priv->addr = i2c_address; + priv->xtal_freq = xtal_freq; + + info("Fitipower FC0012 successfully attached."); + + fe->tuner_priv = priv; + + memcpy(&fe->ops.tuner_ops, &fc0012_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(fc0012_attach); + +MODULE_DESCRIPTION("Fitipower FC0012 silicon tuner driver"); +MODULE_AUTHOR("Hans-Frieder Vogt "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.6"); diff --git a/drivers/media/common/tuners/fc0012.h b/drivers/media/common/tuners/fc0012.h new file mode 100644 index 000000000000..4dbd5efe8845 --- /dev/null +++ b/drivers/media/common/tuners/fc0012.h @@ -0,0 +1,44 @@ +/* + * Fitipower FC0012 tuner driver - include + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 _FC0012_H_ +#define _FC0012_H_ + +#include "dvb_frontend.h" +#include "fc001x-common.h" + +#if defined(CONFIG_MEDIA_TUNER_FC0012) || \ + (defined(CONFIG_MEDIA_TUNER_FC0012_MODULE) && defined(MODULE)) +extern struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq); +#else +static inline struct dvb_frontend *fc0012_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/fc0013-priv.h b/drivers/media/common/tuners/fc0013-priv.h new file mode 100644 index 000000000000..bfd49dedea22 --- /dev/null +++ b/drivers/media/common/tuners/fc0013-priv.h @@ -0,0 +1,44 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 _FC0013_PRIV_H_ +#define _FC0013_PRIV_H_ + +#define LOG_PREFIX "fc0013" + +#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) + +struct fc0013_priv { + struct i2c_adapter *i2c; + u8 addr; + u8 dual_master; + u8 xtal_freq; + + u32 frequency; + u32 bandwidth; +}; + +#endif diff --git a/drivers/media/common/tuners/fc0013.c b/drivers/media/common/tuners/fc0013.c new file mode 100644 index 000000000000..bd8f0f1e8f3b --- /dev/null +++ b/drivers/media/common/tuners/fc0013.c @@ -0,0 +1,634 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * partially based on driver code from Fitipower + * Copyright (C) 2010 Fitipower Integrated Technology Inc + * + * 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 "fc0013.h" +#include "fc0013-priv.h" + +static int fc0013_writereg(struct fc0013_priv *priv, u8 reg, u8 val) +{ + u8 buf[2] = {reg, val}; + struct i2c_msg msg = { + .addr = priv->addr, .flags = 0, .buf = buf, .len = 2 + }; + + if (i2c_transfer(priv->i2c, &msg, 1) != 1) { + err("I2C write reg failed, reg: %02x, val: %02x", reg, val); + return -EREMOTEIO; + } + return 0; +} + +static int fc0013_readreg(struct fc0013_priv *priv, u8 reg, u8 *val) +{ + struct i2c_msg msg[2] = { + { .addr = priv->addr, .flags = 0, .buf = ®, .len = 1 }, + { .addr = priv->addr, .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + if (i2c_transfer(priv->i2c, msg, 2) != 2) { + err("I2C read reg failed, reg: %02x", reg); + return -EREMOTEIO; + } + return 0; +} + +static int fc0013_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + return 0; +} + +static int fc0013_init(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int i, ret = 0; + unsigned char reg[] = { + 0x00, /* reg. 0x00: dummy */ + 0x09, /* reg. 0x01 */ + 0x16, /* reg. 0x02 */ + 0x00, /* reg. 0x03 */ + 0x00, /* reg. 0x04 */ + 0x17, /* reg. 0x05 */ + 0x02, /* reg. 0x06 */ + 0x0a, /* reg. 0x07: CHECK */ + 0xff, /* reg. 0x08: AGC Clock divide by 256, AGC gain 1/256, + Loop Bw 1/8 */ + 0x6f, /* reg. 0x09: enable LoopThrough */ + 0xb8, /* reg. 0x0a: Disable LO Test Buffer */ + 0x82, /* reg. 0x0b: CHECK */ + 0xfc, /* reg. 0x0c: depending on AGC Up-Down mode, may need 0xf8 */ + 0x01, /* reg. 0x0d: AGC Not Forcing & LNA Forcing, may need 0x02 */ + 0x00, /* reg. 0x0e */ + 0x00, /* reg. 0x0f */ + 0x00, /* reg. 0x10 */ + 0x00, /* reg. 0x11 */ + 0x00, /* reg. 0x12 */ + 0x00, /* reg. 0x13 */ + 0x50, /* reg. 0x14: DVB-t High Gain, UHF. + Middle Gain: 0x48, Low Gain: 0x40 */ + 0x01, /* reg. 0x15 */ + }; + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + case FC_XTAL_28_8_MHZ: + reg[0x07] |= 0x20; + break; + case FC_XTAL_36_MHZ: + default: + break; + } + + if (priv->dual_master) + reg[0x0c] |= 0x02; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + for (i = 1; i < sizeof(reg); i++) { + ret = fc0013_writereg(priv, i, reg[i]); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (ret) + err("fc0013_writereg failed: %d", ret); + + return ret; +} + +static int fc0013_sleep(struct dvb_frontend *fe) +{ + /* nothing to do here */ + return 0; +} + +int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + u8 rc_cal; + int val; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* push rc_cal value, get rc_cal value */ + ret = fc0013_writereg(priv, 0x10, 0x00); + if (ret) + goto error_out; + + /* get rc_cal value */ + ret = fc0013_readreg(priv, 0x10, &rc_cal); + if (ret) + goto error_out; + + rc_cal &= 0x0f; + + val = (int)rc_cal + rc_val; + + /* forcing rc_cal */ + ret = fc0013_writereg(priv, 0x0d, 0x11); + if (ret) + goto error_out; + + /* modify rc_cal value */ + if (val > 15) + ret = fc0013_writereg(priv, 0x10, 0x0f); + else if (val < 0) + ret = fc0013_writereg(priv, 0x10, 0x00); + else + ret = fc0013_writereg(priv, 0x10, (u8)val); + +error_out: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return ret; +} +EXPORT_SYMBOL(fc0013_rc_cal_add); + +int fc0013_rc_cal_reset(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0013_writereg(priv, 0x0d, 0x01); + if (!ret) + ret = fc0013_writereg(priv, 0x10, 0x00); + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + return ret; +} +EXPORT_SYMBOL(fc0013_rc_cal_reset); + +static int fc0013_set_vhf_track(struct fc0013_priv *priv, u32 freq) +{ + int ret; + u8 tmp; + + ret = fc0013_readreg(priv, 0x1d, &tmp); + if (ret) + goto error_out; + tmp &= 0xe3; + if (freq <= 177500) { /* VHF Track: 7 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); + } else if (freq <= 184500) { /* VHF Track: 6 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x18); + } else if (freq <= 191500) { /* VHF Track: 5 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x14); + } else if (freq <= 198500) { /* VHF Track: 4 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x10); + } else if (freq <= 205500) { /* VHF Track: 3 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x0c); + } else if (freq <= 219500) { /* VHF Track: 2 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x08); + } else if (freq < 300000) { /* VHF Track: 1 */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x04); + } else { /* UHF and GPS */ + ret = fc0013_writereg(priv, 0x1d, tmp | 0x1c); + } + if (ret) + goto error_out; +error_out: + return ret; +} + +static int fc0013_set_params(struct dvb_frontend *fe) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int i, ret = 0; + struct dtv_frontend_properties *p = &fe->dtv_property_cache; + u32 freq = p->frequency / 1000; + u32 delsys = p->delivery_system; + unsigned char reg[7], am, pm, multi, tmp; + unsigned long f_vco; + unsigned short xtal_freq_khz_2, xin, xdiv; + int vco_select = false; + + if (fe->callback) { + ret = fe->callback(priv->i2c, DVB_FRONTEND_COMPONENT_TUNER, + FC_FE_CALLBACK_VHF_ENABLE, (freq > 300000 ? 0 : 1)); + if (ret) + goto exit; + } + + switch (priv->xtal_freq) { + case FC_XTAL_27_MHZ: + xtal_freq_khz_2 = 27000 / 2; + break; + case FC_XTAL_36_MHZ: + xtal_freq_khz_2 = 36000 / 2; + break; + case FC_XTAL_28_8_MHZ: + default: + xtal_freq_khz_2 = 28800 / 2; + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + /* set VHF track */ + ret = fc0013_set_vhf_track(priv, freq); + if (ret) + goto exit; + + if (freq < 300000) { + /* enable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp | 0x10); + if (ret) + goto exit; + + /* disable UHF & disable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, tmp & 0x1f); + if (ret) + goto exit; + } else if (freq <= 862000) { + /* disable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp & 0xef); + if (ret) + goto exit; + + /* enable UHF & disable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x40); + if (ret) + goto exit; + } else { + /* disable VHF filter */ + ret = fc0013_readreg(priv, 0x07, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x07, tmp & 0xef); + if (ret) + goto exit; + + /* disable UHF & enable GPS */ + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto exit; + ret = fc0013_writereg(priv, 0x14, (tmp & 0x1f) | 0x20); + if (ret) + goto exit; + } + + /* select frequency divider and the frequency of VCO */ + if (freq < 37084) { /* freq * 96 < 3560000 */ + multi = 96; + reg[5] = 0x82; + reg[6] = 0x00; + } else if (freq < 55625) { /* freq * 64 < 3560000 */ + multi = 64; + reg[5] = 0x02; + reg[6] = 0x02; + } else if (freq < 74167) { /* freq * 48 < 3560000 */ + multi = 48; + reg[5] = 0x42; + reg[6] = 0x00; + } else if (freq < 111250) { /* freq * 32 < 3560000 */ + multi = 32; + reg[5] = 0x82; + reg[6] = 0x02; + } else if (freq < 148334) { /* freq * 24 < 3560000 */ + multi = 24; + reg[5] = 0x22; + reg[6] = 0x00; + } else if (freq < 222500) { /* freq * 16 < 3560000 */ + multi = 16; + reg[5] = 0x42; + reg[6] = 0x02; + } else if (freq < 296667) { /* freq * 12 < 3560000 */ + multi = 12; + reg[5] = 0x12; + reg[6] = 0x00; + } else if (freq < 445000) { /* freq * 8 < 3560000 */ + multi = 8; + reg[5] = 0x22; + reg[6] = 0x02; + } else if (freq < 593334) { /* freq * 6 < 3560000 */ + multi = 6; + reg[5] = 0x0a; + reg[6] = 0x00; + } else if (freq < 950000) { /* freq * 4 < 3800000 */ + multi = 4; + reg[5] = 0x12; + reg[6] = 0x02; + } else { + multi = 2; + reg[5] = 0x0a; + reg[6] = 0x02; + } + + f_vco = freq * multi; + + if (f_vco >= 3060000) { + reg[6] |= 0x08; + vco_select = true; + } + + if (freq >= 45000) { + /* From divided value (XDIV) determined the FA and FP value */ + xdiv = (unsigned short)(f_vco / xtal_freq_khz_2); + if ((f_vco - xdiv * xtal_freq_khz_2) >= (xtal_freq_khz_2 / 2)) + xdiv++; + + pm = (unsigned char)(xdiv / 8); + am = (unsigned char)(xdiv - (8 * pm)); + + if (am < 2) { + reg[1] = am + 8; + reg[2] = pm - 1; + } else { + reg[1] = am; + reg[2] = pm; + } + } else { + /* fix for frequency less than 45 MHz */ + reg[1] = 0x06; + reg[2] = 0x11; + } + + /* fix clock out */ + reg[6] |= 0x20; + + /* From VCO frequency determines the XIN ( fractional part of Delta + Sigma PLL) and divided value (XDIV) */ + xin = (unsigned short)(f_vco - (f_vco / xtal_freq_khz_2) * xtal_freq_khz_2); + xin = (xin << 15) / xtal_freq_khz_2; + if (xin >= 16384) + xin += 32768; + + reg[3] = xin >> 8; + reg[4] = xin & 0xff; + + if (delsys == SYS_DVBT) { + reg[6] &= 0x3f; /* bits 6 and 7 describe the bandwidth */ + switch (p->bandwidth_hz) { + case 6000000: + reg[6] |= 0x80; + break; + case 7000000: + reg[6] |= 0x40; + break; + case 8000000: + default: + break; + } + } else { + err("%s: modulation type not supported!", __func__); + return -EINVAL; + } + + /* modified for Realtek demod */ + reg[5] |= 0x07; + + for (i = 1; i <= 6; i++) { + ret = fc0013_writereg(priv, i, reg[i]); + if (ret) + goto exit; + } + + ret = fc0013_readreg(priv, 0x11, &tmp); + if (ret) + goto exit; + if (multi == 64) + ret = fc0013_writereg(priv, 0x11, tmp | 0x04); + else + ret = fc0013_writereg(priv, 0x11, tmp & 0xfb); + if (ret) + goto exit; + + /* VCO Calibration */ + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + + /* VCO Re-Calibration if needed */ + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + + if (!ret) { + msleep(10); + ret = fc0013_readreg(priv, 0x0e, &tmp); + } + if (ret) + goto exit; + + /* vco selection */ + tmp &= 0x3f; + + if (vco_select) { + if (tmp > 0x3c) { + reg[6] &= ~0x08; + ret = fc0013_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + } + } else { + if (tmp < 0x02) { + reg[6] |= 0x08; + ret = fc0013_writereg(priv, 0x06, reg[6]); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x80); + if (!ret) + ret = fc0013_writereg(priv, 0x0e, 0x00); + } + } + + priv->frequency = p->frequency; + priv->bandwidth = p->bandwidth_hz; + +exit: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static int fc0013_get_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + struct fc0013_priv *priv = fe->tuner_priv; + *frequency = priv->frequency; + return 0; +} + +static int fc0013_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + /* always ? */ + *frequency = 0; + return 0; +} + +static int fc0013_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth) +{ + struct fc0013_priv *priv = fe->tuner_priv; + *bandwidth = priv->bandwidth; + return 0; +} + +#define INPUT_ADC_LEVEL -8 + +static int fc0013_get_rf_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct fc0013_priv *priv = fe->tuner_priv; + int ret; + unsigned char tmp; + int int_temp, lna_gain, int_lna, tot_agc_gain, power; + const int fc0013_lna_gain_table[] = { + /* low gain */ + -63, -58, -99, -73, + -63, -65, -54, -60, + /* middle gain */ + 71, 70, 68, 67, + 65, 63, 61, 58, + /* high gain */ + 197, 191, 188, 186, + 184, 182, 181, 179, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open I2C-gate */ + + ret = fc0013_writereg(priv, 0x13, 0x00); + if (ret) + goto err; + + ret = fc0013_readreg(priv, 0x13, &tmp); + if (ret) + goto err; + int_temp = tmp; + + ret = fc0013_readreg(priv, 0x14, &tmp); + if (ret) + goto err; + lna_gain = tmp & 0x1f; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ + + if (lna_gain < ARRAY_SIZE(fc0013_lna_gain_table)) { + int_lna = fc0013_lna_gain_table[lna_gain]; + tot_agc_gain = (abs((int_temp >> 5) - 7) - 2 + + (int_temp & 0x1f)) * 2; + power = INPUT_ADC_LEVEL - tot_agc_gain - int_lna / 10; + + if (power >= 45) + *strength = 255; /* 100% */ + else if (power < -95) + *strength = 0; + else + *strength = (power + 95) * 255 / 140; + + *strength |= *strength << 8; + } else { + ret = -1; + } + + goto exit; + +err: + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close I2C-gate */ +exit: + if (ret) + warn("%s: failed: %d", __func__, ret); + return ret; +} + +static const struct dvb_tuner_ops fc0013_tuner_ops = { + .info = { + .name = "Fitipower FC0013", + + .frequency_min = 37000000, /* estimate */ + .frequency_max = 1680000000, /* CHECK */ + .frequency_step = 0, + }, + + .release = fc0013_release, + + .init = fc0013_init, + .sleep = fc0013_sleep, + + .set_params = fc0013_set_params, + + .get_frequency = fc0013_get_frequency, + .get_if_frequency = fc0013_get_if_frequency, + .get_bandwidth = fc0013_get_bandwidth, + + .get_rf_strength = fc0013_get_rf_strength, +}; + +struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + struct fc0013_priv *priv = NULL; + + priv = kzalloc(sizeof(struct fc0013_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->i2c = i2c; + priv->dual_master = dual_master; + priv->addr = i2c_address; + priv->xtal_freq = xtal_freq; + + info("Fitipower FC0013 successfully attached."); + + fe->tuner_priv = priv; + + memcpy(&fe->ops.tuner_ops, &fc0013_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + return fe; +} +EXPORT_SYMBOL(fc0013_attach); + +MODULE_DESCRIPTION("Fitipower FC0013 silicon tuner driver"); +MODULE_AUTHOR("Hans-Frieder Vogt "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.2"); diff --git a/drivers/media/common/tuners/fc0013.h b/drivers/media/common/tuners/fc0013.h new file mode 100644 index 000000000000..594efd64aeec --- /dev/null +++ b/drivers/media/common/tuners/fc0013.h @@ -0,0 +1,57 @@ +/* + * Fitipower FC0013 tuner driver + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 _FC0013_H_ +#define _FC0013_H_ + +#include "dvb_frontend.h" +#include "fc001x-common.h" + +#if defined(CONFIG_MEDIA_TUNER_FC0013) || \ + (defined(CONFIG_MEDIA_TUNER_FC0013_MODULE) && defined(MODULE)) +extern struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq); +extern int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val); +extern int fc0013_rc_cal_reset(struct dvb_frontend *fe); +#else +static inline struct dvb_frontend *fc0013_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, + u8 i2c_address, int dual_master, + enum fc001x_xtal_freq xtal_freq) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} + +static inline int fc0013_rc_cal_add(struct dvb_frontend *fe, int rc_val) +{ + return 0; +} + +static inline int fc0013_rc_cal_reset(struct dvb_frontend *fe) +{ + return 0; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/fc001x-common.h b/drivers/media/common/tuners/fc001x-common.h new file mode 100644 index 000000000000..718818156934 --- /dev/null +++ b/drivers/media/common/tuners/fc001x-common.h @@ -0,0 +1,39 @@ +/* + * Fitipower FC0012 & FC0013 tuner driver - common defines + * + * Copyright (C) 2012 Hans-Frieder Vogt + * + * 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 _FC001X_COMMON_H_ +#define _FC001X_COMMON_H_ + +enum fc001x_xtal_freq { + FC_XTAL_27_MHZ, /* 27000000 */ + FC_XTAL_28_8_MHZ, /* 28800000 */ + FC_XTAL_36_MHZ, /* 36000000 */ +}; + +/* + * enum fc001x_fe_callback_commands - Frontend callbacks + * + * @FC_FE_CALLBACK_VHF_ENABLE: enable VHF or UHF + */ +enum fc001x_fe_callback_commands { + FC_FE_CALLBACK_VHF_ENABLE, +}; + +#endif diff --git a/drivers/media/common/tuners/tua9001.c b/drivers/media/common/tuners/tua9001.c new file mode 100644 index 000000000000..de2607084672 --- /dev/null +++ b/drivers/media/common/tuners/tua9001.c @@ -0,0 +1,215 @@ +/* + * Infineon TUA 9001 silicon tuner 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "tua9001.h" +#include "tua9001_priv.h" + +/* write register */ +static int tua9001_wr_reg(struct tua9001_priv *priv, u8 reg, u16 val) +{ + int ret; + u8 buf[3] = { reg, (val >> 8) & 0xff, (val >> 0) & 0xff }; + struct i2c_msg msg[1] = { + { + .addr = priv->cfg->i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + ret = i2c_transfer(priv->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + printk(KERN_WARNING "%s: I2C wr failed=%d reg=%02x\n", + __func__, ret, reg); + ret = -EREMOTEIO; + } + + return ret; +} + +static int tua9001_release(struct dvb_frontend *fe) +{ + kfree(fe->tuner_priv); + fe->tuner_priv = NULL; + + return 0; +} + +static int tua9001_init(struct dvb_frontend *fe) +{ + struct tua9001_priv *priv = fe->tuner_priv; + int ret = 0; + u8 i; + struct reg_val data[] = { + { 0x1e, 0x6512 }, + { 0x25, 0xb888 }, + { 0x39, 0x5460 }, + { 0x3b, 0x00c0 }, + { 0x3a, 0xf000 }, + { 0x08, 0x0000 }, + { 0x32, 0x0030 }, + { 0x41, 0x703a }, + { 0x40, 0x1c78 }, + { 0x2c, 0x1c00 }, + { 0x36, 0xc013 }, + { 0x37, 0x6f18 }, + { 0x27, 0x0008 }, + { 0x2a, 0x0001 }, + { 0x34, 0x0a40 }, + }; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); + if (ret) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + + if (ret < 0) + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int tua9001_set_params(struct dvb_frontend *fe) +{ + struct tua9001_priv *priv = fe->tuner_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i; + u16 val; + u32 frequency; + struct reg_val data[2]; + + pr_debug("%s: delivery_system=%d frequency=%d bandwidth_hz=%d\n", + __func__, c->delivery_system, c->frequency, + c->bandwidth_hz); + + switch (c->delivery_system) { + case SYS_DVBT: + switch (c->bandwidth_hz) { + case 8000000: + val = 0x0000; + break; + case 7000000: + val = 0x1000; + break; + case 6000000: + val = 0x2000; + break; + case 5000000: + val = 0x3000; + break; + default: + ret = -EINVAL; + goto err; + } + break; + default: + ret = -EINVAL; + goto err; + } + + data[0].reg = 0x04; + data[0].val = val; + + frequency = (c->frequency - 150000000); + frequency /= 100; + frequency *= 48; + frequency /= 10000; + + data[1].reg = 0x1f; + data[1].val = frequency; + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c-gate */ + + for (i = 0; i < ARRAY_SIZE(data); i++) { + ret = tua9001_wr_reg(priv, data[i].reg, data[i].val); + if (ret < 0) + break; + } + + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c-gate */ + +err: + if (ret < 0) + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int tua9001_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) +{ + *frequency = 0; /* Zero-IF */ + + return 0; +} + +static const struct dvb_tuner_ops tua9001_tuner_ops = { + .info = { + .name = "Infineon TUA 9001", + + .frequency_min = 170000000, + .frequency_max = 862000000, + .frequency_step = 0, + }, + + .release = tua9001_release, + + .init = tua9001_init, + .set_params = tua9001_set_params, + + .get_if_frequency = tua9001_get_if_frequency, +}; + +struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg) +{ + struct tua9001_priv *priv = NULL; + + priv = kzalloc(sizeof(struct tua9001_priv), GFP_KERNEL); + if (priv == NULL) + return NULL; + + priv->cfg = cfg; + priv->i2c = i2c; + + printk(KERN_INFO "Infineon TUA 9001 successfully attached."); + + memcpy(&fe->ops.tuner_ops, &tua9001_tuner_ops, + sizeof(struct dvb_tuner_ops)); + + fe->tuner_priv = priv; + return fe; +} +EXPORT_SYMBOL(tua9001_attach); + +MODULE_DESCRIPTION("Infineon TUA 9001 silicon tuner driver"); +MODULE_AUTHOR("Antti Palosaari "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/common/tuners/tua9001.h b/drivers/media/common/tuners/tua9001.h new file mode 100644 index 000000000000..38d6ae76b1d6 --- /dev/null +++ b/drivers/media/common/tuners/tua9001.h @@ -0,0 +1,46 @@ +/* + * Infineon TUA 9001 silicon tuner 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TUA9001_H +#define TUA9001_H + +#include "dvb_frontend.h" + +struct tua9001_config { + /* + * I2C address + */ + u8 i2c_addr; +}; + +#if defined(CONFIG_MEDIA_TUNER_TUA9001) || \ + (defined(CONFIG_MEDIA_TUNER_TUA9001_MODULE) && defined(MODULE)) +extern struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg); +#else +static inline struct dvb_frontend *tua9001_attach(struct dvb_frontend *fe, + struct i2c_adapter *i2c, struct tua9001_config *cfg) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/media/common/tuners/tua9001_priv.h b/drivers/media/common/tuners/tua9001_priv.h new file mode 100644 index 000000000000..73cc1ce0575c --- /dev/null +++ b/drivers/media/common/tuners/tua9001_priv.h @@ -0,0 +1,34 @@ +/* + * Infineon TUA 9001 silicon tuner 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef TUA9001_PRIV_H +#define TUA9001_PRIV_H + +struct reg_val { + u8 reg; + u16 val; +}; + +struct tua9001_priv { + struct tua9001_config *cfg; + struct i2c_adapter *i2c; +}; + +#endif diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c index eab2ea424200..dcca42ca57be 100644 --- a/drivers/media/common/tuners/xc5000.c +++ b/drivers/media/common/tuners/xc5000.c @@ -54,7 +54,7 @@ struct xc5000_priv { struct list_head hybrid_tuner_instance_list; u32 if_khz; - u32 xtal_khz; + u16 xtal_khz; u32 freq_hz; u32 bandwidth; u8 video_standard; @@ -631,7 +631,10 @@ static int xc5000_fwupload(struct dvb_frontend *fe) ret = xc_load_i2c_sequence(fe, fw->data); if (XC_RESULT_SUCCESS == ret) ret = xc_set_xtal(fe); - printk(KERN_INFO "xc5000: firmware upload complete...\n"); + if (XC_RESULT_SUCCESS == ret) + printk(KERN_INFO "xc5000: firmware upload complete...\n"); + else + printk(KERN_ERR "xc5000: firmware upload failed...\n"); } out: diff --git a/drivers/media/common/tuners/xc5000.h b/drivers/media/common/tuners/xc5000.h index 39a73bf01406..b1a547494625 100644 --- a/drivers/media/common/tuners/xc5000.h +++ b/drivers/media/common/tuners/xc5000.h @@ -34,7 +34,7 @@ struct xc5000_config { u8 i2c_address; u32 if_khz; u8 radio_input; - u32 xtal_khz; + u16 xtal_khz; int chip_id; }; diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c index 48e48e8af55a..66f52f116b60 100644 --- a/drivers/media/dvb/bt8xx/dst_ca.c +++ b/drivers/media/dvb/bt8xx/dst_ca.c @@ -477,7 +477,6 @@ static int dst_check_ca_pmt(struct dst_state *state, struct ca_msg *p_ca_message static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, void __user *arg) { int i = 0; - unsigned int ca_message_header_len; u32 command = 0; struct ca_msg *hw_buffer; @@ -496,7 +495,6 @@ static int ca_send_message(struct dst_state *state, struct ca_msg *p_ca_message, if (p_ca_message->msg) { - ca_message_header_len = p_ca_message->length; /* Restore it back when you are done */ /* EN50221 tag */ command = 0; diff --git a/drivers/media/dvb/ddbridge/ddbridge-core.c b/drivers/media/dvb/ddbridge/ddbridge-core.c index d88c4aa7d24d..131b938e9e81 100644 --- a/drivers/media/dvb/ddbridge/ddbridge-core.c +++ b/drivers/media/dvb/ddbridge/ddbridge-core.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -1696,7 +1695,7 @@ static struct pci_driver ddb_pci_driver = { .name = "DDBridge", .id_table = ddb_id_tbl, .probe = ddb_probe, - .remove = ddb_remove, + .remove = __devexit_p(ddb_remove), }; static __init int module_init_ddbridge(void) diff --git a/drivers/media/dvb/dvb-core/dvb_demux.c b/drivers/media/dvb/dvb-core/dvb_demux.c index faa3671b649e..d82469f842e2 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.c +++ b/drivers/media/dvb/dvb-core/dvb_demux.c @@ -568,6 +568,16 @@ void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count) } EXPORT_SYMBOL(dvb_dmx_swfilter_204); +void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, size_t count) +{ + spin_lock(&demux->lock); + + demux->feed->cb.ts(buf, count, NULL, 0, &demux->feed->feed.ts, DMX_OK); + + spin_unlock(&demux->lock); +} +EXPORT_SYMBOL(dvb_dmx_swfilter_raw); + static struct dvb_demux_filter *dvb_dmx_filter_alloc(struct dvb_demux *demux) { int i; diff --git a/drivers/media/dvb/dvb-core/dvb_demux.h b/drivers/media/dvb/dvb-core/dvb_demux.h index a7d876fd02dd..fa7188a253aa 100644 --- a/drivers/media/dvb/dvb-core/dvb_demux.h +++ b/drivers/media/dvb/dvb-core/dvb_demux.h @@ -145,5 +145,7 @@ void dvb_dmx_swfilter_packets(struct dvb_demux *dvbdmx, const u8 *buf, void dvb_dmx_swfilter(struct dvb_demux *demux, const u8 *buf, size_t count); void dvb_dmx_swfilter_204(struct dvb_demux *demux, const u8 *buf, size_t count); +void dvb_dmx_swfilter_raw(struct dvb_demux *demux, const u8 *buf, + size_t count); #endif /* _DVB_DEMUX_H_ */ diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c index cb888d835a89..aebcdf221dda 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.c +++ b/drivers/media/dvb/dvb-core/dvb_frontend.c @@ -182,13 +182,13 @@ static enum dvbv3_emulation_type dvbv3_type(u32 delivery_system) case SYS_DMBTH: return DVBV3_OFDM; case SYS_ATSC: + case SYS_ATSCMH: case SYS_DVBC_ANNEX_B: return DVBV3_ATSC; case SYS_UNDEFINED: case SYS_ISDBC: case SYS_DVBH: case SYS_DAB: - case SYS_ATSCMH: default: /* * Doesn't know how to emulate those types and/or @@ -1030,6 +1030,25 @@ static struct dtv_cmds_h dtv_cmds[DTV_MAX_COMMAND + 1] = { _DTV_CMD(DTV_HIERARCHY, 0, 0), _DTV_CMD(DTV_ENUM_DELSYS, 0, 0), + + _DTV_CMD(DTV_ATSCMH_PARADE_ID, 1, 0), + _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 1, 0), + + _DTV_CMD(DTV_ATSCMH_FIC_VER, 0, 0), + _DTV_CMD(DTV_ATSCMH_PARADE_ID, 0, 0), + _DTV_CMD(DTV_ATSCMH_NOG, 0, 0), + _DTV_CMD(DTV_ATSCMH_TNOG, 0, 0), + _DTV_CMD(DTV_ATSCMH_SGN, 0, 0), + _DTV_CMD(DTV_ATSCMH_PRC, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_FRAME_MODE, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_FRAME_ENSEMBLE, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_PRI, 0, 0), + _DTV_CMD(DTV_ATSCMH_RS_CODE_MODE_SEC, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_BLOCK_MODE, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_A, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_B, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_C, 0, 0), + _DTV_CMD(DTV_ATSCMH_SCCC_CODE_MODE_D, 0, 0), }; static void dtv_property_dump(struct dtv_property *tvp) @@ -1121,6 +1140,8 @@ static int dtv_property_cache_sync(struct dvb_frontend *fe, case DVBV3_ATSC: dprintk("%s() Preparing ATSC req\n", __func__); c->modulation = p->u.vsb.modulation; + if (c->delivery_system == SYS_ATSCMH) + break; if ((c->modulation == VSB_8) || (c->modulation == VSB_16)) c->delivery_system = SYS_ATSC; else @@ -1367,6 +1388,54 @@ static int dtv_property_process_get(struct dvb_frontend *fe, case DTV_DVBT2_PLP_ID: tvp->u.data = c->dvbt2_plp_id; break; + + /* ATSC-MH */ + case DTV_ATSCMH_FIC_VER: + tvp->u.data = fe->dtv_property_cache.atscmh_fic_ver; + break; + case DTV_ATSCMH_PARADE_ID: + tvp->u.data = fe->dtv_property_cache.atscmh_parade_id; + break; + case DTV_ATSCMH_NOG: + tvp->u.data = fe->dtv_property_cache.atscmh_nog; + break; + case DTV_ATSCMH_TNOG: + tvp->u.data = fe->dtv_property_cache.atscmh_tnog; + break; + case DTV_ATSCMH_SGN: + tvp->u.data = fe->dtv_property_cache.atscmh_sgn; + break; + case DTV_ATSCMH_PRC: + tvp->u.data = fe->dtv_property_cache.atscmh_prc; + break; + case DTV_ATSCMH_RS_FRAME_MODE: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_mode; + break; + case DTV_ATSCMH_RS_FRAME_ENSEMBLE: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_frame_ensemble; + break; + case DTV_ATSCMH_RS_CODE_MODE_PRI: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_pri; + break; + case DTV_ATSCMH_RS_CODE_MODE_SEC: + tvp->u.data = fe->dtv_property_cache.atscmh_rs_code_mode_sec; + break; + case DTV_ATSCMH_SCCC_BLOCK_MODE: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_block_mode; + break; + case DTV_ATSCMH_SCCC_CODE_MODE_A: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_a; + break; + case DTV_ATSCMH_SCCC_CODE_MODE_B: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_b; + break; + case DTV_ATSCMH_SCCC_CODE_MODE_C: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_c; + break; + case DTV_ATSCMH_SCCC_CODE_MODE_D: + tvp->u.data = fe->dtv_property_cache.atscmh_sccc_code_mode_d; + break; + default: return -EINVAL; } @@ -1708,6 +1777,15 @@ static int dtv_property_process_set(struct dvb_frontend *fe, case DTV_DVBT2_PLP_ID: c->dvbt2_plp_id = tvp->u.data; break; + + /* ATSC-MH */ + case DTV_ATSCMH_PARADE_ID: + fe->dtv_property_cache.atscmh_parade_id = tvp->u.data; + break; + case DTV_ATSCMH_RS_FRAME_ENSEMBLE: + fe->dtv_property_cache.atscmh_rs_frame_ensemble = tvp->u.data; + break; + default: return -EINVAL; } diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h index d63a8215fe03..e929d5697b87 100644 --- a/drivers/media/dvb/dvb-core/dvb_frontend.h +++ b/drivers/media/dvb/dvb-core/dvb_frontend.h @@ -372,6 +372,24 @@ struct dtv_frontend_properties { /* DVB-T2 specifics */ u32 dvbt2_plp_id; + + /* ATSC-MH specifics */ + u8 atscmh_fic_ver; + u8 atscmh_parade_id; + u8 atscmh_nog; + u8 atscmh_tnog; + u8 atscmh_sgn; + u8 atscmh_prc; + + u8 atscmh_rs_frame_mode; + u8 atscmh_rs_frame_ensemble; + u8 atscmh_rs_code_mode_pri; + u8 atscmh_rs_code_mode_sec; + u8 atscmh_sccc_block_mode; + u8 atscmh_sccc_code_mode_a; + u8 atscmh_sccc_code_mode_b; + u8 atscmh_sccc_code_mode_c; + u8 atscmh_sccc_code_mode_d; }; struct dvb_frontend { diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 63bf45679f98..a26949336b3d 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -409,6 +409,7 @@ config DVB_USB_MXL111SF tristate "MxL111SF DTV USB2.0 support" depends on DVB_USB select DVB_LGDT3305 if !DVB_FE_CUSTOMISE + select DVB_LG2160 if !DVB_FE_CUSTOMISE select VIDEO_TVEEPROM help Say Y here to support the MxL111SF USB2.0 DTV receiver. @@ -422,3 +423,15 @@ config DVB_USB_RTL28XXU select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE help Say Y here to support the Realtek RTL28xxU DVB USB receiver. + +config DVB_USB_AF9035 + tristate "Afatech AF9035 DVB-T USB2.0 support" + depends on DVB_USB + select DVB_AF9033 + select MEDIA_TUNER_TUA9001 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_FC0011 if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE + select MEDIA_TUNER_TDA18218 if !MEDIA_TUNER_CUSTOMISE + help + Say Y here to support the Afatech AF9035 based DVB USB receiver. + diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile index b76acb5387e6..b667ac39a4e3 100644 --- a/drivers/media/dvb/dvb-usb/Makefile +++ b/drivers/media/dvb/dvb-usb/Makefile @@ -110,6 +110,9 @@ obj-$(CONFIG_DVB_USB_MXL111SF) += mxl111sf-tuner.o dvb-usb-rtl28xxu-objs = rtl28xxu.o obj-$(CONFIG_DVB_USB_RTL28XXU) += dvb-usb-rtl28xxu.o +dvb-usb-af9035-objs = af9035.o +obj-$(CONFIG_DVB_USB_AF9035) += dvb-usb-af9035.o + ccflags-y += -I$(srctree)/drivers/media/dvb/dvb-core ccflags-y += -I$(srctree)/drivers/media/dvb/frontends/ # due to tuner-xc3028 diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c index 7e70ea50ef26..677fed79b01e 100644 --- a/drivers/media/dvb/dvb-usb/af9015.c +++ b/drivers/media/dvb/dvb-usb/af9015.c @@ -244,8 +244,7 @@ static int af9015_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], u8 uninitialized_var(mbox), addr_len; struct req_t req; -/* TODO: implement bus lock - +/* The bus lock is needed because there is two tuners both using same I2C-address. Due to that the only way to select correct tuner is use demodulator I2C-gate. @@ -789,7 +788,7 @@ static void af9015_set_remote_config(struct usb_device *udev, /* try to load remote based USB ID */ if (!props->rc.core.rc_codes) props->rc.core.rc_codes = af9015_rc_setup_match( - (vid << 16) + pid, af9015_rc_setup_usbids); + (vid << 16) | pid, af9015_rc_setup_usbids); /* try to load remote based USB iManufacturer string */ if (!props->rc.core.rc_codes && vid == USB_VID_AFATECH) { @@ -1220,8 +1219,8 @@ static int af9015_af9013_frontend_attach(struct dvb_usb_adapter *adap) } /* attach demodulator */ - adap->fe_adap[0].fe = dvb_attach(af9013_attach, &af9015_af9013_config[adap->id], - &adap->dev->i2c_adap); + adap->fe_adap[0].fe = dvb_attach(af9013_attach, + &af9015_af9013_config[adap->id], &adap->dev->i2c_adap); /* * AF9015 firmware does not like if it gets interrupted by I2C adapter @@ -1324,14 +1323,15 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap) switch (af9015_af9013_config[adap->id].tuner) { case AF9013_TUNER_MT2060: case AF9013_TUNER_MT2060_2: - ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, - &af9015_mt2060_config, + ret = dvb_attach(mt2060_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9015_mt2060_config, af9015_config.mt2060_if1[adap->id]) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_QT1010: case AF9013_TUNER_QT1010A: - ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, &adap->dev->i2c_adap, + ret = dvb_attach(qt1010_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9015_qt1010_config) == NULL ? -ENODEV : 0; break; case AF9013_TUNER_TDA18271: @@ -1434,69 +1434,85 @@ enum af9015_usb_table_entry { }; static struct usb_device_id af9015_usb_table[] = { - [AFATECH_9015] = - {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)}, - [AFATECH_9016] = - {USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)}, - [WINFAST_DTV_GOLD] = - {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)}, - [PINNACLE_PCTV_71E] = - {USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)}, - [KWORLD_PLUSTV_399U] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)}, - [TINYTWIN] = {USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN)}, - [AZUREWAVE_TU700] = - {USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700)}, - [TERRATEC_AF9015] = {USB_DEVICE(USB_VID_TERRATEC, + [AFATECH_9015] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9015)}, + [AFATECH_9016] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9015_9016)}, + [WINFAST_DTV_GOLD] = { + USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_GOLD)}, + [PINNACLE_PCTV_71E] = { + USB_DEVICE(USB_VID_PINNACLE, USB_PID_PINNACLE_PCTV71E)}, + [KWORLD_PLUSTV_399U] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U)}, + [TINYTWIN] = { + USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_TINYTWIN)}, + [AZUREWAVE_TU700] = { + USB_DEVICE(USB_VID_VISIONPLUS, USB_PID_AZUREWAVE_AD_TU700)}, + [TERRATEC_AF9015] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2)}, - [KWORLD_PLUSTV_PC160] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)}, - [AVERTV_VOLAR_X] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)}, - [XTENSIONS_380U] = - {USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, - [MSI_DIGIVOX_DUO] = - {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, - [AVERTV_VOLAR_X_REV2] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, - [TELESTAR_STARSTICK_2] = - {USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, - [AVERMEDIA_A309_USB] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)}, - [MSI_DIGIVOX_MINI_III] = - {USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)}, - [KWORLD_E396] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)}, - [KWORLD_E39B] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)}, - [KWORLD_E395] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)}, - [TREKSTOR_DVBT] = {USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)}, - [AVERTV_A850] = {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)}, - [AVERTV_A805] = {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805)}, - [CONCEPTRONIC_CTVDIGRCU] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)}, - [KWORLD_MC810] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)}, - [GENIUS_TVGO_DVB_T03] = - {USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)}, - [KWORLD_399U_2] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, - [KWORLD_PC160_T] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)}, - [SVEON_STV20] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, - [TINYTWIN_2] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)}, - [WINFAST_DTV2000DS] = - {USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)}, - [KWORLD_UB383_T] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)}, - [KWORLD_E39A] = - {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)}, - [AVERMEDIA_A815M] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)}, - [CINERGY_T_STICK_RC] = {USB_DEVICE(USB_VID_TERRATEC, + [KWORLD_PLUSTV_PC160] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_2T)}, + [AVERTV_VOLAR_X] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X)}, + [XTENSIONS_380U] = { + USB_DEVICE(USB_VID_XTENSIONS, USB_PID_XTENSIONS_XD_380)}, + [MSI_DIGIVOX_DUO] = { + USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGIVOX_DUO)}, + [AVERTV_VOLAR_X_REV2] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_VOLAR_X_2)}, + [TELESTAR_STARSTICK_2] = { + USB_DEVICE(USB_VID_TELESTAR, USB_PID_TELESTAR_STARSTICK_2)}, + [AVERMEDIA_A309_USB] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)}, + [MSI_DIGIVOX_MINI_III] = { + USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)}, + [KWORLD_E396] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)}, + [KWORLD_E39B] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)}, + [KWORLD_E395] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_3)}, + [TREKSTOR_DVBT] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_TREKSTOR_DVBT)}, + [AVERTV_A850] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850)}, + [AVERTV_A805] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A805)}, + [CONCEPTRONIC_CTVDIGRCU] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_CONCEPTRONIC_CTVDIGRCU)}, + [KWORLD_MC810] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_MC810)}, + [GENIUS_TVGO_DVB_T03] = { + USB_DEVICE(USB_VID_KYE, USB_PID_GENIUS_TVGO_DVB_T03)}, + [KWORLD_399U_2] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_399U_2)}, + [KWORLD_PC160_T] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_PC160_T)}, + [SVEON_STV20] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV20)}, + [TINYTWIN_2] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_TINYTWIN_2)}, + [WINFAST_DTV2000DS] = { + USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV2000DS)}, + [KWORLD_UB383_T] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_UB383_T)}, + [KWORLD_E39A] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_4)}, + [AVERMEDIA_A815M] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A815M)}, + [CINERGY_T_STICK_RC] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_RC)}, - [CINERGY_T_DUAL_RC] = {USB_DEVICE(USB_VID_TERRATEC, + [CINERGY_T_DUAL_RC] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC)}, - [AVERTV_A850T] = - {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)}, - [TINYTWIN_3] = {USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)}, - [SVEON_STV22] = {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)}, + [AVERTV_A850T] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A850T)}, + [TINYTWIN_3] = { + USB_DEVICE(USB_VID_GTEK, USB_PID_TINYTWIN_3)}, + [SVEON_STV22] = { + USB_DEVICE(USB_VID_KWORLD_2, USB_PID_SVEON_STV22)}, { } }; MODULE_DEVICE_TABLE(usb, af9015_usb_table); @@ -1516,43 +1532,44 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, - .fe = {{ - .caps = DVB_USB_ADAP_HAS_PID_FILTER | - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .num_frontends = 1, + .fe = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, - .pid_filter_count = 32, - .pid_filter = af9015_pid_filter, - .pid_filter_ctrl = af9015_pid_filter_ctrl, + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x84, - }, - }}, - }, - { - .num_frontends = 1, - .fe = {{ - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x85, - .u = { - .bulk = { - .buffersize = - TS_USB20_FRAME_SIZE, - } + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + } + }, + }, + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + .u = { + .bulk = { + .buffersize = TS_USB20_FRAME_SIZE, + } + } + }, } }, - }}, } }, @@ -1575,102 +1592,67 @@ static struct dvb_usb_device_properties af9015_properties[] = { .cold_ids = { &af9015_usb_table[AFATECH_9015], &af9015_usb_table[AFATECH_9016], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Leadtek WinFast DTV Dongle Gold", .cold_ids = { &af9015_usb_table[WINFAST_DTV_GOLD], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Pinnacle PCTV 71e", .cold_ids = { &af9015_usb_table[PINNACLE_PCTV_71E], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld PlusTV Dual DVB-T Stick " \ "(DVB-T 399U)", .cold_ids = { &af9015_usb_table[KWORLD_PLUSTV_399U], &af9015_usb_table[KWORLD_399U_2], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "DigitalNow TinyTwin DVB-T Receiver", .cold_ids = { &af9015_usb_table[TINYTWIN], &af9015_usb_table[TINYTWIN_2], &af9015_usb_table[TINYTWIN_3], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TwinHan AzureWave AD-TU700(704J)", .cold_ids = { &af9015_usb_table[AZUREWAVE_TU700], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TerraTec Cinergy T USB XE", .cold_ids = { &af9015_usb_table[TERRATEC_AF9015], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld PlusTV Dual DVB-T PCI " \ "(DVB-T PC160-2T)", .cold_ids = { &af9015_usb_table[KWORLD_PLUSTV_PC160], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AVerMedia AVerTV DVB-T Volar X", .cold_ids = { &af9015_usb_table[AVERTV_VOLAR_X], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TerraTec Cinergy T Stick RC", .cold_ids = { &af9015_usb_table[CINERGY_T_STICK_RC], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TerraTec Cinergy T Stick Dual RC", .cold_ids = { &af9015_usb_table[CINERGY_T_DUAL_RC], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AverMedia AVerTV Red HD+ (A850T)", .cold_ids = { &af9015_usb_table[AVERTV_A850T], - NULL }, - .warm_ids = {NULL}, }, } }, { @@ -1686,43 +1668,44 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, - .fe = {{ - .caps = DVB_USB_ADAP_HAS_PID_FILTER | - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .num_frontends = 1, + .fe = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, - .pid_filter_count = 32, - .pid_filter = af9015_pid_filter, - .pid_filter_ctrl = af9015_pid_filter_ctrl, + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x84, - }, - }}, - }, - { - .num_frontends = 1, - .fe = {{ - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x85, - .u = { - .bulk = { - .buffersize = - TS_USB20_FRAME_SIZE, - } + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + } + }, + }, + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + .u = { + .bulk = { + .buffersize = TS_USB20_FRAME_SIZE, + } + } + }, } }, - }}, } }, @@ -1744,51 +1727,33 @@ static struct dvb_usb_device_properties af9015_properties[] = { .name = "Xtensions XD-380", .cold_ids = { &af9015_usb_table[XTENSIONS_380U], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "MSI DIGIVOX Duo", .cold_ids = { &af9015_usb_table[MSI_DIGIVOX_DUO], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Fujitsu-Siemens Slim Mobile USB DVB-T", .cold_ids = { &af9015_usb_table[AVERTV_VOLAR_X_REV2], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Telestar Starstick 2", .cold_ids = { &af9015_usb_table[TELESTAR_STARSTICK_2], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AVerMedia A309", .cold_ids = { &af9015_usb_table[AVERMEDIA_A309_USB], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "MSI Digi VOX mini III", .cold_ids = { &af9015_usb_table[MSI_DIGIVOX_MINI_III], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld USB DVB-T TV Stick II " \ "(VS-DVB-T 395U)", .cold_ids = { @@ -1796,34 +1761,23 @@ static struct dvb_usb_device_properties af9015_properties[] = { &af9015_usb_table[KWORLD_E39B], &af9015_usb_table[KWORLD_E395], &af9015_usb_table[KWORLD_E39A], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "TrekStor DVB-T USB Stick", .cold_ids = { &af9015_usb_table[TREKSTOR_DVBT], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AverMedia AVerTV Volar Black HD " \ "(A850)", .cold_ids = { &af9015_usb_table[AVERTV_A850], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Sveon STV22 Dual USB DVB-T Tuner HDTV", .cold_ids = { &af9015_usb_table[SVEON_STV22], - NULL }, - .warm_ids = {NULL}, }, } }, { @@ -1839,43 +1793,44 @@ static struct dvb_usb_device_properties af9015_properties[] = { .num_adapters = 2, .adapter = { { - .num_frontends = 1, - .fe = {{ - .caps = DVB_USB_ADAP_HAS_PID_FILTER | - DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, + .num_frontends = 1, + .fe = { + { + .caps = DVB_USB_ADAP_HAS_PID_FILTER | + DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF, - .pid_filter_count = 32, - .pid_filter = af9015_pid_filter, - .pid_filter_ctrl = af9015_pid_filter_ctrl, + .pid_filter_count = 32, + .pid_filter = af9015_pid_filter, + .pid_filter_ctrl = af9015_pid_filter_ctrl, - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x84, - }, - }}, - }, - { - .num_frontends = 1, - .fe = {{ - .frontend_attach = - af9015_af9013_frontend_attach, - .tuner_attach = af9015_tuner_attach, - .stream = { - .type = USB_BULK, - .count = 6, - .endpoint = 0x85, - .u = { - .bulk = { - .buffersize = - TS_USB20_FRAME_SIZE, - } + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + }, + } + }, + }, + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9015_af9013_frontend_attach, + .tuner_attach = af9015_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x85, + .u = { + .bulk = { + .buffersize = TS_USB20_FRAME_SIZE, + } + } + }, } }, - }}, } }, @@ -1897,76 +1852,50 @@ static struct dvb_usb_device_properties af9015_properties[] = { .name = "AverMedia AVerTV Volar GPS 805 (A805)", .cold_ids = { &af9015_usb_table[AVERTV_A805], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Conceptronic USB2.0 DVB-T CTVDIGRCU " \ "V3.0", .cold_ids = { &af9015_usb_table[CONCEPTRONIC_CTVDIGRCU], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld Digial MC-810", .cold_ids = { &af9015_usb_table[KWORLD_MC810], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Genius TVGo DVB-T03", .cold_ids = { &af9015_usb_table[GENIUS_TVGO_DVB_T03], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld PlusTV DVB-T PCI Pro Card " \ "(DVB-T PC160-T)", .cold_ids = { &af9015_usb_table[KWORLD_PC160_T], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Sveon STV20 Tuner USB DVB-T HDTV", .cold_ids = { &af9015_usb_table[SVEON_STV20], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "Leadtek WinFast DTV2000DS", .cold_ids = { &af9015_usb_table[WINFAST_DTV2000DS], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "KWorld USB DVB-T Stick Mobile " \ "(UB383-T)", .cold_ids = { &af9015_usb_table[KWORLD_UB383_T], - NULL }, - .warm_ids = {NULL}, - }, - { + }, { .name = "AverMedia AVerTV Volar M (A815Mac)", .cold_ids = { &af9015_usb_table[AVERMEDIA_A815M], - NULL }, - .warm_ids = {NULL}, }, } }, @@ -2019,5 +1948,5 @@ static struct usb_driver af9015_usb_driver = { module_usb_driver(af9015_usb_driver); MODULE_AUTHOR("Antti Palosaari "); -MODULE_DESCRIPTION("Driver for Afatech AF9015 DVB-T"); +MODULE_DESCRIPTION("Afatech AF9015 driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/af9035.c b/drivers/media/dvb/dvb-usb/af9035.c new file mode 100644 index 000000000000..e83b39d3993c --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9035.c @@ -0,0 +1,1242 @@ +/* + * Afatech AF9035 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "af9035.h" + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); +static DEFINE_MUTEX(af9035_usb_mutex); +static struct dvb_usb_device_properties af9035_properties[2]; +static int af9035_properties_count = ARRAY_SIZE(af9035_properties); + +static u16 af9035_checksum(const u8 *buf, size_t len) +{ + size_t i; + u16 checksum = 0; + + for (i = 1; i < len; i++) { + if (i % 2) + checksum += buf[i] << 8; + else + checksum += buf[i]; + } + checksum = ~checksum; + + return checksum; +} + +static int af9035_ctrl_msg(struct usb_device *udev, struct usb_req *req) +{ +#define BUF_LEN 64 +#define REQ_HDR_LEN 4 /* send header size */ +#define ACK_HDR_LEN 3 /* rece header size */ +#define CHECKSUM_LEN 2 +#define USB_TIMEOUT 2000 + + int ret, msg_len, act_len; + u8 buf[BUF_LEN]; + static u8 seq; /* packet sequence number */ + u16 checksum, tmp_checksum; + + /* buffer overflow check */ + if (req->wlen > (BUF_LEN - REQ_HDR_LEN - CHECKSUM_LEN) || + req->rlen > (BUF_LEN - ACK_HDR_LEN - CHECKSUM_LEN)) { + pr_debug("%s: too much data wlen=%d rlen=%d\n", __func__, + req->wlen, req->rlen); + return -EINVAL; + } + + if (mutex_lock_interruptible(&af9035_usb_mutex) < 0) + return -EAGAIN; + + buf[0] = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN - 1; + buf[1] = req->mbox; + buf[2] = req->cmd; + buf[3] = seq++; + if (req->wlen) + memcpy(&buf[4], req->wbuf, req->wlen); + + /* calc and add checksum */ + checksum = af9035_checksum(buf, buf[0] - 1); + buf[buf[0] - 1] = (checksum >> 8); + buf[buf[0] - 0] = (checksum & 0xff); + + msg_len = REQ_HDR_LEN + req->wlen + CHECKSUM_LEN ; + + /* send req */ + ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, + &act_len, USB_TIMEOUT); + if (ret < 0) + err("bulk message failed=%d (%d/%d)", ret, msg_len, act_len); + else + if (act_len != msg_len) + ret = -EIO; /* all data is not send */ + if (ret < 0) + goto err_mutex_unlock; + + /* no ack for those packets */ + if (req->cmd == CMD_FW_DL) + goto exit_mutex_unlock; + + /* receive ack and data if read req */ + msg_len = ACK_HDR_LEN + req->rlen + CHECKSUM_LEN; + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, + &act_len, USB_TIMEOUT); + if (ret < 0) { + err("recv bulk message failed=%d", ret); + ret = -EIO; + goto err_mutex_unlock; + } + + if (act_len != msg_len) { + err("recv bulk message truncated (%d != %d)", act_len, msg_len); + ret = -EIO; + goto err_mutex_unlock; + } + + /* verify checksum */ + checksum = af9035_checksum(buf, act_len - 2); + tmp_checksum = (buf[act_len - 2] << 8) | buf[act_len - 1]; + if (tmp_checksum != checksum) { + err("%s: command=%02x checksum mismatch (%04x != %04x)", + __func__, req->cmd, tmp_checksum, checksum); + ret = -EIO; + goto err_mutex_unlock; + } + + /* check status */ + if (buf[2]) { + pr_debug("%s: command=%02x failed fw error=%d\n", __func__, + req->cmd, buf[2]); + ret = -EIO; + goto err_mutex_unlock; + } + + /* read request, copy returned data to return buf */ + if (req->rlen) + memcpy(req->rbuf, &buf[ACK_HDR_LEN], req->rlen); + +err_mutex_unlock: +exit_mutex_unlock: + mutex_unlock(&af9035_usb_mutex); + + return ret; +} + +/* write multiple registers */ +static int af9035_wr_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) +{ + u8 wbuf[6 + len]; + u8 mbox = (reg >> 16) & 0xff; + struct usb_req req = { CMD_MEM_WR, mbox, sizeof(wbuf), wbuf, 0, NULL }; + + wbuf[0] = len; + wbuf[1] = 2; + wbuf[2] = 0; + wbuf[3] = 0; + wbuf[4] = (reg >> 8) & 0xff; + wbuf[5] = (reg >> 0) & 0xff; + memcpy(&wbuf[6], val, len); + + return af9035_ctrl_msg(d->udev, &req); +} + +/* read multiple registers */ +static int af9035_rd_regs(struct dvb_usb_device *d, u32 reg, u8 *val, int len) +{ + u8 wbuf[] = { len, 2, 0, 0, (reg >> 8) & 0xff, reg & 0xff }; + u8 mbox = (reg >> 16) & 0xff; + struct usb_req req = { CMD_MEM_RD, mbox, sizeof(wbuf), wbuf, len, val }; + + return af9035_ctrl_msg(d->udev, &req); +} + +/* write single register */ +static int af9035_wr_reg(struct dvb_usb_device *d, u32 reg, u8 val) +{ + return af9035_wr_regs(d, reg, &val, 1); +} + +/* read single register */ +static int af9035_rd_reg(struct dvb_usb_device *d, u32 reg, u8 *val) +{ + return af9035_rd_regs(d, reg, val, 1); +} + +/* write single register with mask */ +static int af9035_wr_reg_mask(struct dvb_usb_device *d, u32 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = af9035_rd_regs(d, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return af9035_wr_regs(d, reg, &val, 1); +} + +static int af9035_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msg[], int num) +{ + struct dvb_usb_device *d = i2c_get_adapdata(adap); + struct state *state = d->priv; + int ret; + + if (mutex_lock_interruptible(&d->i2c_mutex) < 0) + return -EAGAIN; + + /* + * I2C sub header is 5 bytes long. Meaning of those bytes are: + * 0: data len + * 1: I2C addr << 1 + * 2: reg addr len + * byte 3 and 4 can be used as reg addr + * 3: reg addr MSB + * used when reg addr len is set to 2 + * 4: reg addr LSB + * used when reg addr len is set to 1 or 2 + * + * For the simplify we do not use register addr at all. + * NOTE: As a firmware knows tuner type there is very small possibility + * there could be some tuner I2C hacks done by firmware and this may + * lead problems if firmware expects those bytes are used. + */ + if (num == 2 && !(msg[0].flags & I2C_M_RD) && + (msg[1].flags & I2C_M_RD)) { + if (msg[0].len > 40 || msg[1].len > 40) { + /* TODO: correct limits > 40 */ + ret = -EOPNOTSUPP; + } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { + /* integrated demod */ + u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; + ret = af9035_rd_regs(d, reg, &msg[1].buf[0], + msg[1].len); + } else { + /* I2C */ + u8 buf[5 + msg[0].len]; + struct usb_req req = { CMD_I2C_RD, 0, sizeof(buf), + buf, msg[1].len, msg[1].buf }; + buf[0] = msg[1].len; + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + ret = af9035_ctrl_msg(d->udev, &req); + } + } else if (num == 1 && !(msg[0].flags & I2C_M_RD)) { + if (msg[0].len > 40) { + /* TODO: correct limits > 40 */ + ret = -EOPNOTSUPP; + } else if (msg[0].addr == state->af9033_config[0].i2c_addr) { + /* integrated demod */ + u32 reg = msg[0].buf[0] << 16 | msg[0].buf[1] << 8 | + msg[0].buf[2]; + ret = af9035_wr_regs(d, reg, &msg[0].buf[3], + msg[0].len - 3); + } else { + /* I2C */ + u8 buf[5 + msg[0].len]; + struct usb_req req = { CMD_I2C_WR, 0, sizeof(buf), buf, + 0, NULL }; + buf[0] = msg[0].len; + buf[1] = msg[0].addr << 1; + buf[2] = 0x00; /* reg addr len */ + buf[3] = 0x00; /* reg addr MSB */ + buf[4] = 0x00; /* reg addr LSB */ + memcpy(&buf[5], msg[0].buf, msg[0].len); + ret = af9035_ctrl_msg(d->udev, &req); + } + } else { + /* + * We support only two kind of I2C transactions: + * 1) 1 x read + 1 x write + * 2) 1 x write + */ + ret = -EOPNOTSUPP; + } + + mutex_unlock(&d->i2c_mutex); + + if (ret < 0) + return ret; + else + return num; +} + +static u32 af9035_i2c_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm af9035_i2c_algo = { + .master_xfer = af9035_i2c_master_xfer, + .functionality = af9035_i2c_functionality, +}; + +#define AF9035_POLL 250 +static int af9035_rc_query(struct dvb_usb_device *d) +{ + unsigned int key; + unsigned char b[4]; + int ret; + struct usb_req req = { CMD_IR_GET, 0, 0, NULL, 4, b }; + + ret = af9035_ctrl_msg(d->udev, &req); + if (ret < 0) + goto err; + + if ((b[2] + b[3]) == 0xff) { + if ((b[0] + b[1]) == 0xff) { + /* NEC */ + key = b[0] << 8 | b[2]; + } else { + /* ext. NEC */ + key = b[0] << 16 | b[1] << 8 | b[2]; + } + } else { + key = b[0] << 24 | b[1] << 16 | b[2] << 8 | b[3]; + } + + rc_keydown(d->rc_dev, key, 0); + +err: + /* ignore errors */ + return 0; +} + +static int af9035_init(struct dvb_usb_device *d) +{ + struct state *state = d->priv; + int ret, i; + u16 frame_size = 87 * 188 / 4; + u8 packet_size = 512 / 4; + struct reg_val_mask tab[] = { + { 0x80f99d, 0x01, 0x01 }, + { 0x80f9a4, 0x01, 0x01 }, + { 0x00dd11, 0x00, 0x20 }, + { 0x00dd11, 0x00, 0x40 }, + { 0x00dd13, 0x00, 0x20 }, + { 0x00dd13, 0x00, 0x40 }, + { 0x00dd11, 0x20, 0x20 }, + { 0x00dd88, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd89, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0c, packet_size, 0xff}, + { 0x00dd11, state->dual_mode << 6, 0x40 }, + { 0x00dd8a, (frame_size >> 0) & 0xff, 0xff}, + { 0x00dd8b, (frame_size >> 8) & 0xff, 0xff}, + { 0x00dd0d, packet_size, 0xff }, + { 0x80f9a3, 0x00, 0x01 }, + { 0x80f9cd, 0x00, 0x01 }, + { 0x80f99d, 0x00, 0x01 }, + { 0x80f9a4, 0x00, 0x01 }, + }; + + pr_debug("%s: USB speed=%d frame_size=%04x packet_size=%02x\n", + __func__, d->udev->speed, frame_size, packet_size); + + /* init endpoints */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = af9035_wr_reg_mask(d, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_identify_state(struct usb_device *udev, + struct dvb_usb_device_properties *props, + struct dvb_usb_device_description **desc, + int *cold) +{ + int ret; + u8 wbuf[1] = { 1 }; + u8 rbuf[4]; + struct usb_req req = { CMD_FW_QUERYINFO, 0, sizeof(wbuf), wbuf, + sizeof(rbuf), rbuf }; + + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + pr_debug("%s: reply=%02x %02x %02x %02x\n", __func__, + rbuf[0], rbuf[1], rbuf[2], rbuf[3]); + if (rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3]) + *cold = 0; + else + *cold = 1; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_download_firmware(struct usb_device *udev, + const struct firmware *fw) +{ + int ret, i, j, len; + u8 wbuf[1]; + u8 rbuf[4]; + struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_dl = { CMD_FW_DL, 0, 0, wbuf, 0, NULL }; + struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + u8 hdr_core; + u16 hdr_addr, hdr_data_len, hdr_checksum; + #define MAX_DATA 58 + #define HDR_SIZE 7 + + /* + * Thanks to Daniel Glöckner about that info! + * + * byte 0: MCS 51 core + * There are two inside the AF9035 (1=Link and 2=OFDM) with separate + * address spaces + * byte 1-2: Big endian destination address + * byte 3-4: Big endian number of data bytes following the header + * byte 5-6: Big endian header checksum, apparently ignored by the chip + * Calculated as ~(h[0]*256+h[1]+h[2]*256+h[3]+h[4]*256) + */ + + for (i = fw->size; i > HDR_SIZE;) { + hdr_core = fw->data[fw->size - i + 0]; + hdr_addr = fw->data[fw->size - i + 1] << 8; + hdr_addr |= fw->data[fw->size - i + 2] << 0; + hdr_data_len = fw->data[fw->size - i + 3] << 8; + hdr_data_len |= fw->data[fw->size - i + 4] << 0; + hdr_checksum = fw->data[fw->size - i + 5] << 8; + hdr_checksum |= fw->data[fw->size - i + 6] << 0; + + pr_debug("%s: core=%d addr=%04x data_len=%d checksum=%04x\n", + __func__, hdr_core, hdr_addr, hdr_data_len, + hdr_checksum); + + if (((hdr_core != 1) && (hdr_core != 2)) || + (hdr_data_len > i)) { + pr_debug("%s: bad firmware\n", __func__); + break; + } + + /* download begin packet */ + req.cmd = CMD_FW_DL_BEGIN; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + /* download firmware packet(s) */ + for (j = HDR_SIZE + hdr_data_len; j > 0; j -= MAX_DATA) { + len = j; + if (len > MAX_DATA) + len = MAX_DATA; + req_fw_dl.wlen = len; + req_fw_dl.wbuf = (u8 *) &fw->data[fw->size - i + + HDR_SIZE + hdr_data_len - j]; + ret = af9035_ctrl_msg(udev, &req_fw_dl); + if (ret < 0) + goto err; + } + + /* download end packet */ + req.cmd = CMD_FW_DL_END; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + i -= hdr_data_len + HDR_SIZE; + + pr_debug("%s: data uploaded=%zu\n", __func__, fw->size - i); + } + + /* firmware loaded, request boot */ + req.cmd = CMD_FW_BOOT; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + /* ensure firmware starts */ + wbuf[0] = 1; + ret = af9035_ctrl_msg(udev, &req_fw_ver); + if (ret < 0) + goto err; + + if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { + info("firmware did not run"); + ret = -ENODEV; + goto err; + } + + info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2], + rbuf[3]); + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_download_firmware_it9135(struct usb_device *udev, + const struct firmware *fw) +{ + int ret, i, i_prev; + u8 wbuf[1]; + u8 rbuf[4]; + struct usb_req req = { 0, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_dl = { CMD_FW_SCATTER_WR, 0, 0, NULL, 0, NULL }; + struct usb_req req_fw_ver = { CMD_FW_QUERYINFO, 0, 1, wbuf, 4, rbuf } ; + #define HDR_SIZE 7 + + /* + * There seems to be following firmware header. Meaning of bytes 0-3 + * is unknown. + * + * 0: 3 + * 1: 0, 1 + * 2: 0 + * 3: 1, 2, 3 + * 4: addr MSB + * 5: addr LSB + * 6: count of data bytes ? + */ + + for (i = HDR_SIZE, i_prev = 0; i <= fw->size; i++) { + if (i == fw->size || + (fw->data[i + 0] == 0x03 && + (fw->data[i + 1] == 0x00 || + fw->data[i + 1] == 0x01) && + fw->data[i + 2] == 0x00)) { + req_fw_dl.wlen = i - i_prev; + req_fw_dl.wbuf = (u8 *) &fw->data[i_prev]; + i_prev = i; + ret = af9035_ctrl_msg(udev, &req_fw_dl); + if (ret < 0) + goto err; + + pr_debug("%s: data uploaded=%d\n", __func__, i); + } + } + + /* firmware loaded, request boot */ + req.cmd = CMD_FW_BOOT; + ret = af9035_ctrl_msg(udev, &req); + if (ret < 0) + goto err; + + /* ensure firmware starts */ + wbuf[0] = 1; + ret = af9035_ctrl_msg(udev, &req_fw_ver); + if (ret < 0) + goto err; + + if (!(rbuf[0] || rbuf[1] || rbuf[2] || rbuf[3])) { + info("firmware did not run"); + ret = -ENODEV; + goto err; + } + + info("firmware version=%d.%d.%d.%d", rbuf[0], rbuf[1], rbuf[2], + rbuf[3]); + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* abuse that callback as there is no better one for reading eeprom */ +static int af9035_read_mac_address(struct dvb_usb_device *d, u8 mac[6]) +{ + struct state *state = d->priv; + int ret, i, eeprom_shift = 0; + u8 tmp; + u16 tmp16; + + /* check if there is dual tuners */ + ret = af9035_rd_reg(d, EEPROM_DUAL_MODE, &tmp); + if (ret < 0) + goto err; + + state->dual_mode = tmp; + pr_debug("%s: dual mode=%d\n", __func__, state->dual_mode); + + for (i = 0; i < af9035_properties[0].num_adapters; i++) { + /* tuner */ + ret = af9035_rd_reg(d, EEPROM_1_TUNER_ID + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + state->af9033_config[i].tuner = tmp; + pr_debug("%s: [%d]tuner=%02x\n", __func__, i, tmp); + + switch (tmp) { + case AF9033_TUNER_TUA9001: + case AF9033_TUNER_FC0011: + case AF9033_TUNER_MXL5007T: + case AF9033_TUNER_TDA18218: + state->af9033_config[i].spec_inv = 1; + break; + default: + warn("tuner ID=%02x not supported, please report!", + tmp); + }; + + /* tuner IF frequency */ + ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_L + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + tmp16 = tmp; + + ret = af9035_rd_reg(d, EEPROM_1_IFFREQ_H + eeprom_shift, &tmp); + if (ret < 0) + goto err; + + tmp16 |= tmp << 8; + + pr_debug("%s: [%d]IF=%d\n", __func__, i, tmp16); + + eeprom_shift = 0x10; /* shift for the 2nd tuner params */ + } + + /* get demod clock */ + ret = af9035_rd_reg(d, 0x00d800, &tmp); + if (ret < 0) + goto err; + + tmp = (tmp >> 0) & 0x0f; + + for (i = 0; i < af9035_properties[0].num_adapters; i++) + state->af9033_config[i].clock = clock_lut[tmp]; + + ret = af9035_rd_reg(d, EEPROM_IR_MODE, &tmp); + if (ret < 0) + goto err; + pr_debug("%s: ir_mode=%02x\n", __func__, tmp); + + /* don't activate rc if in HID mode or if not available */ + if (tmp == 5) { + ret = af9035_rd_reg(d, EEPROM_IR_TYPE, &tmp); + if (ret < 0) + goto err; + pr_debug("%s: ir_type=%02x\n", __func__, tmp); + + switch (tmp) { + case 0: /* NEC */ + default: + d->props.rc.core.protocol = RC_TYPE_NEC; + d->props.rc.core.allowed_protos = RC_TYPE_NEC; + break; + case 1: /* RC6 */ + d->props.rc.core.protocol = RC_TYPE_RC6; + d->props.rc.core.allowed_protos = RC_TYPE_RC6; + break; + } + d->props.rc.core.rc_query = af9035_rc_query; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* abuse that callback as there is no better one for reading eeprom */ +static int af9035_read_mac_address_it9135(struct dvb_usb_device *d, u8 mac[6]) +{ + struct state *state = d->priv; + int ret, i; + u8 tmp; + + state->dual_mode = false; + + /* get demod clock */ + ret = af9035_rd_reg(d, 0x00d800, &tmp); + if (ret < 0) + goto err; + + tmp = (tmp >> 0) & 0x0f; + + for (i = 0; i < af9035_properties[0].num_adapters; i++) + state->af9033_config[i].clock = clock_lut_it9135[tmp]; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_fc0011_tuner_callback(struct dvb_usb_device *d, + int cmd, int arg) +{ + int ret; + + switch (cmd) { + case FC0011_FE_CALLBACK_POWER: + /* Tuner enable */ + ret = af9035_wr_reg_mask(d, 0xd8eb, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ec, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8ed, 1, 1); + if (ret < 0) + goto err; + + /* LED */ + ret = af9035_wr_reg_mask(d, 0xd8d0, 1, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(d, 0xd8d1, 1, 1); + if (ret < 0) + goto err; + + usleep_range(10000, 50000); + break; + case FC0011_FE_CALLBACK_RESET: + ret = af9035_wr_reg(d, 0xd8e9, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0xd8e8, 1); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(d, 0xd8e7, 1); + if (ret < 0) + goto err; + + usleep_range(10000, 20000); + + ret = af9035_wr_reg(d, 0xd8e7, 0); + if (ret < 0) + goto err; + + usleep_range(10000, 20000); + break; + default: + ret = -EINVAL; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9035_tuner_callback(struct dvb_usb_device *d, int cmd, int arg) +{ + struct state *state = d->priv; + + switch (state->af9033_config[0].tuner) { + case AF9033_TUNER_FC0011: + return af9035_fc0011_tuner_callback(d, cmd, arg); + default: + break; + } + + return -ENODEV; +} + +static int af9035_frontend_callback(void *adapter_priv, int component, + int cmd, int arg) +{ + struct i2c_adapter *adap = adapter_priv; + struct dvb_usb_device *d = i2c_get_adapdata(adap); + + switch (component) { + case DVB_FRONTEND_COMPONENT_TUNER: + return af9035_tuner_callback(d, cmd, arg); + default: + break; + } + + return -EINVAL; +} + +static int af9035_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct state *state = adap->dev->priv; + int ret; + + if (!state->af9033_config[adap->id].tuner) { + /* unsupported tuner */ + ret = -ENODEV; + goto err; + } + + if (adap->id == 0) { + state->af9033_config[0].ts_mode = AF9033_TS_MODE_USB; + state->af9033_config[1].ts_mode = AF9033_TS_MODE_SERIAL; + + ret = af9035_wr_reg(adap->dev, 0x00417f, + state->af9033_config[1].i2c_addr); + if (ret < 0) + goto err; + + ret = af9035_wr_reg(adap->dev, 0x00d81a, + state->dual_mode); + if (ret < 0) + goto err; + } + + /* attach demodulator */ + adap->fe_adap[0].fe = dvb_attach(af9033_attach, + &state->af9033_config[adap->id], &adap->dev->i2c_adap); + if (adap->fe_adap[0].fe == NULL) { + ret = -ENODEV; + goto err; + } + + /* disable I2C-gate */ + adap->fe_adap[0].fe->ops.i2c_gate_ctrl = NULL; + adap->fe_adap[0].fe->callback = af9035_frontend_callback; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static struct tua9001_config af9035_tua9001_config = { + .i2c_addr = 0x60, +}; + +static const struct fc0011_config af9035_fc0011_config = { + .i2c_address = 0x60, +}; + +static struct mxl5007t_config af9035_mxl5007t_config = { + .xtal_freq_hz = MxL_XTAL_24_MHZ, + .if_freq_hz = MxL_IF_4_57_MHZ, + .invert_if = 0, + .loop_thru_enable = 0, + .clk_out_enable = 0, + .clk_out_amp = MxL_CLKOUT_AMP_0_94V, +}; + +static struct tda18218_config af9035_tda18218_config = { + .i2c_address = 0x60, + .i2c_wr_max = 21, +}; + +static int af9035_tuner_attach(struct dvb_usb_adapter *adap) +{ + struct state *state = adap->dev->priv; + int ret; + struct dvb_frontend *fe; + + switch (state->af9033_config[adap->id].tuner) { + case AF9033_TUNER_TUA9001: + /* AF9035 gpiot3 = TUA9001 RESETN + AF9035 gpiot2 = TUA9001 RXEN */ + + /* configure gpiot2 and gpiot2 as output */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8ec, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8ed, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e8, 0x01, 0x01); + if (ret < 0) + goto err; + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e9, 0x01, 0x01); + if (ret < 0) + goto err; + + /* reset tuner */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x00, 0x01); + if (ret < 0) + goto err; + + usleep_range(2000, 20000); + + ret = af9035_wr_reg_mask(adap->dev, 0x00d8e7, 0x01, 0x01); + if (ret < 0) + goto err; + + /* activate tuner RX */ + /* TODO: use callback for TUA9001 RXEN */ + ret = af9035_wr_reg_mask(adap->dev, 0x00d8eb, 0x01, 0x01); + if (ret < 0) + goto err; + + /* attach tuner */ + fe = dvb_attach(tua9001_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9035_tua9001_config); + break; + case AF9033_TUNER_FC0011: + fe = dvb_attach(fc0011_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9035_fc0011_config); + break; + case AF9033_TUNER_MXL5007T: + ret = af9035_wr_reg(adap->dev, 0x00d8e0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8e1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8df, 0); + if (ret < 0) + goto err; + + msleep(30); + + ret = af9035_wr_reg(adap->dev, 0x00d8df, 1); + if (ret < 0) + goto err; + + msleep(300); + + ret = af9035_wr_reg(adap->dev, 0x00d8c0, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8c1, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8bf, 0); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b4, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b5, 1); + if (ret < 0) + goto err; + ret = af9035_wr_reg(adap->dev, 0x00d8b3, 1); + if (ret < 0) + goto err; + + /* attach tuner */ + fe = dvb_attach(mxl5007t_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, 0x60, &af9035_mxl5007t_config); + break; + case AF9033_TUNER_TDA18218: + /* attach tuner */ + fe = dvb_attach(tda18218_attach, adap->fe_adap[0].fe, + &adap->dev->i2c_adap, &af9035_tda18218_config); + break; + default: + fe = NULL; + } + + if (fe == NULL) { + ret = -ENODEV; + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +enum af9035_id_entry { + AF9035_15A4_9035, + AF9035_15A4_1000, + AF9035_15A4_1001, + AF9035_15A4_1002, + AF9035_15A4_1003, + AF9035_0CCD_0093, + AF9035_07CA_A835, + AF9035_07CA_B835, + AF9035_07CA_1867, + AF9035_07CA_A867, + AF9035_07CA_0825, +}; + +static struct usb_device_id af9035_id[] = { + [AF9035_15A4_9035] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_9035)}, + [AF9035_15A4_1000] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1000)}, + [AF9035_15A4_1001] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1001)}, + [AF9035_15A4_1002] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1002)}, + [AF9035_15A4_1003] = { + USB_DEVICE(USB_VID_AFATECH, USB_PID_AFATECH_AF9035_1003)}, + [AF9035_0CCD_0093] = { + USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_STICK)}, + [AF9035_07CA_A835] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A835)}, + [AF9035_07CA_B835] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_B835)}, + [AF9035_07CA_1867] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_1867)}, + [AF9035_07CA_A867] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A867)}, + [AF9035_07CA_0825] = { + USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_TWINSTAR)}, + {}, +}; + +MODULE_DEVICE_TABLE(usb, af9035_id); + +static struct dvb_usb_device_properties af9035_properties[] = { + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9035_download_firmware, + .firmware = "dvb-usb-af9035-02.fw", + .no_reconnect = 1, + + .size_of_priv = sizeof(struct state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9035_frontend_attach, + .tuner_attach = af9035_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + .u = { + .bulk = { + .buffersize = (87 * 188), + } + } + } + } + } + } + }, + + .identify_state = af9035_identify_state, + .read_mac_address = af9035_read_mac_address, + + .i2c_algo = &af9035_i2c_algo, + + .rc.core = { + .protocol = RC_TYPE_UNKNOWN, + .module_name = "af9035", + .rc_query = NULL, + .rc_interval = AF9035_POLL, + .allowed_protos = RC_TYPE_UNKNOWN, + .rc_codes = RC_MAP_EMPTY, + }, + .num_device_descs = 5, + .devices = { + { + .name = "Afatech AF9035 reference design", + .cold_ids = { + &af9035_id[AF9035_15A4_9035], + &af9035_id[AF9035_15A4_1000], + &af9035_id[AF9035_15A4_1001], + &af9035_id[AF9035_15A4_1002], + &af9035_id[AF9035_15A4_1003], + }, + }, { + .name = "TerraTec Cinergy T Stick", + .cold_ids = { + &af9035_id[AF9035_0CCD_0093], + }, + }, { + .name = "AVerMedia AVerTV Volar HD/PRO (A835)", + .cold_ids = { + &af9035_id[AF9035_07CA_A835], + &af9035_id[AF9035_07CA_B835], + }, + }, { + .name = "AVerMedia HD Volar (A867)", + .cold_ids = { + &af9035_id[AF9035_07CA_1867], + &af9035_id[AF9035_07CA_A867], + }, + }, { + .name = "AVerMedia Twinstar (A825)", + .cold_ids = { + &af9035_id[AF9035_07CA_0825], + }, + }, + } + }, + { + .caps = DVB_USB_IS_AN_I2C_ADAPTER, + + .usb_ctrl = DEVICE_SPECIFIC, + .download_firmware = af9035_download_firmware_it9135, + .firmware = "dvb-usb-it9135-01.fw", + .no_reconnect = 1, + + .size_of_priv = sizeof(struct state), + + .num_adapters = 1, + .adapter = { + { + .num_frontends = 1, + .fe = { + { + .frontend_attach = af9035_frontend_attach, + .tuner_attach = af9035_tuner_attach, + .stream = { + .type = USB_BULK, + .count = 6, + .endpoint = 0x84, + .u = { + .bulk = { + .buffersize = (87 * 188), + } + } + } + } + } + } + }, + + .identify_state = af9035_identify_state, + .read_mac_address = af9035_read_mac_address_it9135, + + .i2c_algo = &af9035_i2c_algo, + + .num_device_descs = 0, /* disabled as no support for IT9135 */ + .devices = { + { + .name = "ITE Tech. IT9135 reference design", + }, + } + }, +}; + +static int af9035_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + int ret, i; + struct dvb_usb_device *d = NULL; + struct usb_device *udev; + bool found; + + pr_debug("%s: interface=%d\n", __func__, + intf->cur_altsetting->desc.bInterfaceNumber); + + /* interface 0 is used by DVB-T receiver and + interface 1 is for remote controller (HID) */ + if (intf->cur_altsetting->desc.bInterfaceNumber != 0) + return 0; + + /* Dynamic USB ID support. Replaces first device ID with current one. */ + udev = interface_to_usbdev(intf); + + for (i = 0, found = false; i < ARRAY_SIZE(af9035_id) - 1; i++) { + if (af9035_id[i].idVendor == + le16_to_cpu(udev->descriptor.idVendor) && + af9035_id[i].idProduct == + le16_to_cpu(udev->descriptor.idProduct)) { + found = true; + break; + } + } + + if (!found) { + pr_debug("%s: using dynamic ID %04x:%04x\n", __func__, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + af9035_properties[0].devices[0].cold_ids[0]->idVendor = + le16_to_cpu(udev->descriptor.idVendor); + af9035_properties[0].devices[0].cold_ids[0]->idProduct = + le16_to_cpu(udev->descriptor.idProduct); + } + + + for (i = 0; i < af9035_properties_count; i++) { + ret = dvb_usb_device_init(intf, &af9035_properties[i], + THIS_MODULE, &d, adapter_nr); + + if (ret == -ENODEV) + continue; + else + break; + } + + if (ret < 0) + goto err; + + if (d) { + ret = af9035_init(d); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +/* usb specific object needed to register this driver with the usb subsystem */ +static struct usb_driver af9035_usb_driver = { + .name = "dvb_usb_af9035", + .probe = af9035_usb_probe, + .disconnect = dvb_usb_device_exit, + .id_table = af9035_id, +}; + +module_usb_driver(af9035_usb_driver); + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Afatech AF9035 driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/dvb-usb/af9035.h b/drivers/media/dvb/dvb-usb/af9035.h new file mode 100644 index 000000000000..481a1a43dd2a --- /dev/null +++ b/drivers/media/dvb/dvb-usb/af9035.h @@ -0,0 +1,113 @@ +/* + * Afatech AF9035 DVB USB driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9035_H +#define AF9035_H + +/* prefix for dvb-usb log writings */ +#define DVB_USB_LOG_PREFIX "af9035" + +#include "dvb-usb.h" +#include "af9033.h" +#include "tua9001.h" +#include "fc0011.h" +#include "mxl5007t.h" +#include "tda18218.h" + +struct reg_val { + u32 reg; + u8 val; +}; + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +struct usb_req { + u8 cmd; + u8 mbox; + u8 wlen; + u8 *wbuf; + u8 rlen; + u8 *rbuf; +}; + +struct state { + bool dual_mode; + + struct af9033_config af9033_config[2]; +}; + +u32 clock_lut[] = { + 20480000, /* FPGA */ + 16384000, /* 16.38 MHz */ + 20480000, /* 20.48 MHz */ + 36000000, /* 36.00 MHz */ + 30000000, /* 30.00 MHz */ + 26000000, /* 26.00 MHz */ + 28000000, /* 28.00 MHz */ + 32000000, /* 32.00 MHz */ + 34000000, /* 34.00 MHz */ + 24000000, /* 24.00 MHz */ + 22000000, /* 22.00 MHz */ + 12000000, /* 12.00 MHz */ +}; + +u32 clock_lut_it9135[] = { + 12000000, /* 12.00 MHz */ + 20480000, /* 20.48 MHz */ + 36000000, /* 36.00 MHz */ + 30000000, /* 30.00 MHz */ + 26000000, /* 26.00 MHz */ + 28000000, /* 28.00 MHz */ + 32000000, /* 32.00 MHz */ + 34000000, /* 34.00 MHz */ + 24000000, /* 24.00 MHz */ + 22000000, /* 22.00 MHz */ +}; + +/* EEPROM locations */ +#define EEPROM_IR_MODE 0x430d +#define EEPROM_DUAL_MODE 0x4326 +#define EEPROM_IR_TYPE 0x4329 +#define EEPROM_1_IFFREQ_L 0x432d +#define EEPROM_1_IFFREQ_H 0x432e +#define EEPROM_1_TUNER_ID 0x4331 +#define EEPROM_2_IFFREQ_L 0x433d +#define EEPROM_2_IFFREQ_H 0x433e +#define EEPROM_2_TUNER_ID 0x4341 + +/* USB commands */ +#define CMD_MEM_RD 0x00 +#define CMD_MEM_WR 0x01 +#define CMD_I2C_RD 0x02 +#define CMD_I2C_WR 0x03 +#define CMD_IR_GET 0x18 +#define CMD_FW_DL 0x21 +#define CMD_FW_QUERYINFO 0x22 +#define CMD_FW_BOOT 0x23 +#define CMD_FW_DL_BEGIN 0x24 +#define CMD_FW_DL_END 0x25 +#define CMD_FW_SCATTER_WR 0x29 + +#endif diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c index 02290c60f72f..7e9e00fae04e 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_core.c +++ b/drivers/media/dvb/dvb-usb/dib0700_core.c @@ -32,7 +32,7 @@ int dib0700_get_version(struct dvb_usb_device *d, u32 *hwversion, if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } ret = usb_control_msg(d->udev, usb_rcvctrlpipe(d->udev, 0), @@ -118,7 +118,7 @@ int dib0700_set_gpio(struct dvb_usb_device *d, enum dib07x0_gpios gpio, u8 gpio_ if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_GPIO; @@ -139,7 +139,7 @@ static int dib0700_set_usb_xfer_len(struct dvb_usb_device *d, u16 nb_ts_packets) if (st->fw_version >= 0x10201) { if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_USB_XFER_LEN; @@ -178,7 +178,7 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, /* Ensure nobody else hits the i2c bus while we're sending our sequence of messages, (such as the remote control thread) */ if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; + return -EINTR; for (i = 0; i < num; i++) { if (i == 0) { @@ -228,7 +228,8 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg, /* Write request */ if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + mutex_unlock(&d->i2c_mutex); + return -EINTR; } st->buf[0] = REQUEST_NEW_I2C_WRITE; st->buf[1] = msg[i].addr << 1; @@ -271,10 +272,11 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap, int i,len; if (mutex_lock_interruptible(&d->i2c_mutex) < 0) - return -EAGAIN; + return -EINTR; if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + mutex_unlock(&d->i2c_mutex); + return -EINTR; } for (i = 0; i < num; i++) { @@ -369,7 +371,7 @@ static int dib0700_set_clock(struct dvb_usb_device *d, u8 en_pll, if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_CLOCK; @@ -401,7 +403,7 @@ int dib0700_set_i2c_speed(struct dvb_usb_device *d, u16 scl_kHz) if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_I2C_PARAM; @@ -561,7 +563,7 @@ int dib0700_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) if (mutex_lock_interruptible(&adap->dev->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_ENABLE_VIDEO; @@ -611,7 +613,7 @@ int dib0700_change_protocol(struct rc_dev *rc, u64 rc_type) if (mutex_lock_interruptible(&d->usb_mutex) < 0) { err("could not acquire lock"); - return 0; + return -EINTR; } st->buf[0] = REQUEST_SET_RC; diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c index f9e966aa26e7..510001da6e83 100644 --- a/drivers/media/dvb/dvb-usb/dib0700_devices.c +++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c @@ -3569,6 +3569,7 @@ struct usb_device_id dib0700_usb_id_table[] = { { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7090E) }, { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE7790E) }, /* 80 */{ USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_TFE8096P) }, + { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT_2) }, { 0 } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table); @@ -3832,7 +3833,7 @@ struct dvb_usb_device_properties dib0700_devices[] = { }, }, - .num_device_descs = 11, + .num_device_descs = 12, .devices = { { "DiBcom STK7070P reference design", { &dib0700_usb_id_table[15], NULL }, @@ -3878,6 +3879,10 @@ struct dvb_usb_device_properties dib0700_devices[] = { { &dib0700_usb_id_table[50], NULL }, { NULL }, }, + { "Elgato EyeTV DTT rev. 2", + { &dib0700_usb_id_table[81], NULL }, + { NULL }, + }, }, .rc.core = { diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h index 397d8f232731..7a6160bf54ba 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h @@ -76,6 +76,11 @@ #define USB_PID_AFATECH_AF9005 0x9020 #define USB_PID_AFATECH_AF9015_9015 0x9015 #define USB_PID_AFATECH_AF9015_9016 0x9016 +#define USB_PID_AFATECH_AF9035_1000 0x1000 +#define USB_PID_AFATECH_AF9035_1001 0x1001 +#define USB_PID_AFATECH_AF9035_1002 0x1002 +#define USB_PID_AFATECH_AF9035_1003 0x1003 +#define USB_PID_AFATECH_AF9035_9035 0x9035 #define USB_PID_TREKSTOR_DVBT 0x901b #define USB_VID_ALINK_DTU 0xf170 #define USB_PID_ANSONIC_DVBT_USB 0x6000 @@ -152,6 +157,7 @@ #define USB_PID_KWORLD_VSTREAM_WARM 0x17df #define USB_PID_TERRATEC_CINERGY_T_USB_XE 0x0055 #define USB_PID_TERRATEC_CINERGY_T_USB_XE_REV2 0x0069 +#define USB_PID_TERRATEC_CINERGY_T_STICK 0x0093 #define USB_PID_TERRATEC_CINERGY_T_STICK_RC 0x0097 #define USB_PID_TERRATEC_CINERGY_T_STICK_DUAL_RC 0x0099 #define USB_PID_TWINHAN_VP7041_COLD 0x3201 @@ -221,6 +227,11 @@ #define USB_PID_AVERMEDIA_A850T 0x850b #define USB_PID_AVERMEDIA_A805 0xa805 #define USB_PID_AVERMEDIA_A815M 0x815a +#define USB_PID_AVERMEDIA_A835 0xa835 +#define USB_PID_AVERMEDIA_B835 0xb835 +#define USB_PID_AVERMEDIA_1867 0x1867 +#define USB_PID_AVERMEDIA_A867 0xa867 +#define USB_PID_AVERMEDIA_TWINSTAR 0x0825 #define USB_PID_TECHNOTREND_CONNECT_S2400 0x3006 #define USB_PID_TECHNOTREND_CONNECT_CT3650 0x300d #define USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY 0x005a @@ -327,6 +338,7 @@ #define USB_PID_MYGICA_D689 0xd811 #define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011 #define USB_PID_ELGATO_EYETV_DTT 0x0021 +#define USB_PID_ELGATO_EYETV_DTT_2 0x003f #define USB_PID_ELGATO_EYETV_DTT_Dlx 0x0020 #define USB_PID_ELGATO_EYETV_SAT 0x002a #define USB_PID_DVB_T_USB_STICK_HIGH_SPEED_COLD 0x5000 diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c index 53a5c30b51b2..5c8f651344fc 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c +++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c @@ -80,6 +80,14 @@ static void dvb_usb_data_complete_204(struct usb_data_stream *stream, u8 *buffer dvb_dmx_swfilter_204(&adap->demux, buffer, length); } +static void dvb_usb_data_complete_raw(struct usb_data_stream *stream, + u8 *buffer, size_t length) +{ + struct dvb_usb_adapter *adap = stream->user_priv; + if (adap->feedcount > 0 && adap->state & DVB_USB_ADAP_STATE_DVB) + dvb_dmx_swfilter_raw(&adap->demux, buffer, length); +} + int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap) { int i, ret = 0; @@ -90,6 +98,10 @@ int dvb_usb_adapter_stream_init(struct dvb_usb_adapter *adap) adap->fe_adap[i].stream.complete = dvb_usb_data_complete_204; else + if (adap->props.fe[i].caps & DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD) + adap->fe_adap[i].stream.complete = + dvb_usb_data_complete_raw; + else adap->fe_adap[i].stream.complete = dvb_usb_data_complete; adap->fe_adap[i].stream.user_priv = adap; ret = usb_urb_init(&adap->fe_adap[i].stream, diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 6d7d13f9ce68..99f94409efa1 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -141,6 +141,7 @@ struct dvb_usb_adapter_fe_properties { #define DVB_USB_ADAP_PID_FILTER_CAN_BE_TURNED_OFF 0x02 #define DVB_USB_ADAP_NEED_PID_FILTERING 0x04 #define DVB_USB_ADAP_RECEIVES_204_BYTE_TS 0x08 +#define DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD 0x10 int caps; int pid_filter_count; @@ -156,7 +157,7 @@ struct dvb_usb_adapter_fe_properties { int size_of_priv; }; -#define MAX_NO_OF_FE_PER_ADAP 2 +#define MAX_NO_OF_FE_PER_ADAP 3 struct dvb_usb_adapter_properties { int size_of_priv; diff --git a/drivers/media/dvb/dvb-usb/dw2102.c b/drivers/media/dvb/dvb-usb/dw2102.c index 451c5a7adfb2..9382895b1b88 100644 --- a/drivers/media/dvb/dvb-usb/dw2102.c +++ b/drivers/media/dvb/dvb-usb/dw2102.c @@ -148,7 +148,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int i = 0, ret = 0; + int i = 0; u8 buf6[] = {0x2c, 0x05, 0xc0, 0, 0, 0, 0}; u16 value; @@ -162,7 +162,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], /* read stv0299 register */ value = msg[0].buf[0];/* register */ for (i = 0; i < msg[1].len; i++) { - ret = dw210x_op_rw(d->udev, 0xb5, value + i, 0, + dw210x_op_rw(d->udev, 0xb5, value + i, 0, buf6, 2, DW210X_READ_MSG); msg[1].buf[i] = buf6[0]; } @@ -174,7 +174,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], buf6[0] = 0x2a; buf6[1] = msg[0].buf[0]; buf6[2] = msg[0].buf[1]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 3, DW210X_WRITE_MSG); break; case 0x60: @@ -187,17 +187,17 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], buf6[4] = msg[0].buf[1]; buf6[5] = msg[0].buf[2]; buf6[6] = msg[0].buf[3]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 7, DW210X_WRITE_MSG); } else { /* read from tuner */ - ret = dw210x_op_rw(d->udev, 0xb5, 0, 0, + dw210x_op_rw(d->udev, 0xb5, 0, 0, buf6, 1, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; } break; case (DW2102_RC_QUERY): - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, buf6, 2, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; msg[0].buf[1] = buf6[1]; @@ -205,7 +205,7 @@ static int dw2102_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], case (DW2102_VOLTAGE_CTRL): buf6[0] = 0x30; buf6[1] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 2, DW210X_WRITE_MSG); break; } @@ -221,7 +221,6 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret = 0; u8 buf6[] = {0, 0, 0, 0, 0, 0, 0}; if (!d) @@ -235,10 +234,10 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, buf6[0] = msg[0].addr << 1; buf6[1] = msg[0].len; buf6[2] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, msg[0].len + 2, DW210X_WRITE_MSG); /* read si2109 register */ - ret = dw210x_op_rw(d->udev, 0xc3, 0xd0, 0, + dw210x_op_rw(d->udev, 0xc3, 0xd0, 0, buf6, msg[1].len + 2, DW210X_READ_MSG); memcpy(msg[1].buf, buf6 + 2, msg[1].len); @@ -250,11 +249,11 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, buf6[0] = msg[0].addr << 1; buf6[1] = msg[0].len; memcpy(buf6 + 2, msg[0].buf, msg[0].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, + dw210x_op_rw(d->udev, 0xc2, 0, 0, buf6, msg[0].len + 2, DW210X_WRITE_MSG); break; case(DW2102_RC_QUERY): - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, buf6, 2, DW210X_READ_MSG); msg[0].buf[0] = buf6[0]; msg[0].buf[1] = buf6[1]; @@ -262,7 +261,7 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, case(DW2102_VOLTAGE_CTRL): buf6[0] = 0x30; buf6[1] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, buf6, 2, DW210X_WRITE_MSG); break; } @@ -276,7 +275,6 @@ static int dw2102_serit_i2c_transfer(struct i2c_adapter *adap, static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret = 0; if (!d) return -ENODEV; @@ -291,10 +289,10 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; obuf[2] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); /* second read registers */ - ret = dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0, + dw210x_op_rw(d->udev, 0xc3, 0xd1 , 0, ibuf, msg[1].len + 2, DW210X_READ_MSG); memcpy(msg[1].buf, ibuf + 2, msg[1].len); @@ -308,7 +306,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; memcpy(obuf + 2, msg[0].buf, msg[0].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); break; } @@ -318,13 +316,13 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; memcpy(obuf + 2, msg[0].buf, msg[0].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); break; } case(DW2102_RC_QUERY): { u8 ibuf[2]; - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, ibuf, 2, DW210X_READ_MSG); memcpy(msg[0].buf, ibuf , 2); break; @@ -333,7 +331,7 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms u8 obuf[2]; obuf[0] = 0x30; obuf[1] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; } @@ -349,7 +347,6 @@ static int dw2102_earda_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg ms static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret = 0; int len, i, j; if (!d) @@ -361,7 +358,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i switch (msg[j].addr) { case(DW2102_RC_QUERY): { u8 ibuf[2]; - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, ibuf, 2, DW210X_READ_MSG); memcpy(msg[j].buf, ibuf , 2); break; @@ -370,7 +367,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i u8 obuf[2]; obuf[0] = 0x30; obuf[1] = msg[j].buf[0]; - ret = dw210x_op_rw(d->udev, 0xb2, 0, 0, + dw210x_op_rw(d->udev, 0xb2, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; } @@ -382,7 +379,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i if (msg[j].flags == I2C_M_RD) { /* read registers */ u8 ibuf[msg[j].len + 2]; - ret = dw210x_op_rw(d->udev, 0xc3, + dw210x_op_rw(d->udev, 0xc3, (msg[j].addr << 1) + 1, 0, ibuf, msg[j].len + 2, DW210X_READ_MSG); @@ -402,7 +399,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i do { memcpy(obuf + 3, msg[j].buf + i, (len > 16 ? 16 : len)); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG); i += 16; @@ -414,7 +411,7 @@ static int dw2104_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], i obuf[0] = msg[j].addr << 1; obuf[1] = msg[j].len; memcpy(obuf + 2, msg[j].buf, msg[j].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[j].len + 2, DW210X_WRITE_MSG); } @@ -432,7 +429,7 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], int num) { struct dvb_usb_device *d = i2c_get_adapdata(adap); - int ret = 0, i; + int i; if (!d) return -ENODEV; @@ -447,10 +444,10 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; obuf[2] = msg[0].buf[0]; - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); /* second read registers */ - ret = dw210x_op_rw(d->udev, 0xc3, 0x19 , 0, + dw210x_op_rw(d->udev, 0xc3, 0x19 , 0, ibuf, msg[1].len + 2, DW210X_READ_MSG); memcpy(msg[1].buf, ibuf + 2, msg[1].len); @@ -465,13 +462,13 @@ static int dw3101_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[0].addr << 1; obuf[1] = msg[0].len; memcpy(obuf + 2, msg[0].buf, msg[0].len); - ret = dw210x_op_rw(d->udev, 0xc2, 0, 0, + dw210x_op_rw(d->udev, 0xc2, 0, 0, obuf, msg[0].len + 2, DW210X_WRITE_MSG); break; } case(DW2102_RC_QUERY): { u8 ibuf[2]; - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, ibuf, 2, DW210X_READ_MSG); memcpy(msg[0].buf, ibuf , 2); break; @@ -496,7 +493,6 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], { struct dvb_usb_device *d = i2c_get_adapdata(adap); struct usb_device *udev; - int ret = 0; int len, i, j; if (!d) @@ -509,7 +505,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], switch (msg[j].addr) { case (DW2102_RC_QUERY): { u8 ibuf[5]; - ret = dw210x_op_rw(d->udev, 0xb8, 0, 0, + dw210x_op_rw(d->udev, 0xb8, 0, 0, ibuf, 5, DW210X_READ_MSG); memcpy(msg[j].buf, ibuf + 3, 2); break; @@ -519,11 +515,11 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = 1; obuf[1] = msg[j].buf[1];/* off-on */ - ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + dw210x_op_rw(d->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); obuf[0] = 3; obuf[1] = msg[j].buf[0];/* 13v-18v */ - ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + dw210x_op_rw(d->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; } @@ -532,7 +528,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = 5; obuf[1] = msg[j].buf[0]; - ret = dw210x_op_rw(d->udev, 0x8a, 0, 0, + dw210x_op_rw(d->udev, 0x8a, 0, 0, obuf, 2, DW210X_WRITE_MSG); break; } @@ -545,7 +541,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], if (msg[j].flags == I2C_M_RD) { /* read registers */ u8 ibuf[msg[j].len]; - ret = dw210x_op_rw(d->udev, 0x91, 0, 0, + dw210x_op_rw(d->udev, 0x91, 0, 0, ibuf, msg[j].len, DW210X_READ_MSG); memcpy(msg[j].buf, ibuf, msg[j].len); @@ -563,7 +559,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], do { memcpy(obuf + 3, msg[j].buf + i, (len > 16 ? 16 : len)); - ret = dw210x_op_rw(d->udev, 0x80, 0, 0, + dw210x_op_rw(d->udev, 0x80, 0, 0, obuf, (len > 16 ? 16 : len) + 3, DW210X_WRITE_MSG); i += 16; @@ -575,7 +571,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[j + 1].len; obuf[1] = (msg[j].addr << 1); memcpy(obuf + 2, msg[j].buf, msg[j].len); - ret = dw210x_op_rw(d->udev, + dw210x_op_rw(d->udev, udev->descriptor.idProduct == 0x7500 ? 0x92 : 0x90, 0, 0, obuf, msg[j].len + 2, @@ -587,7 +583,7 @@ static int s6x0_i2c_transfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[0] = msg[j].len + 1; obuf[1] = (msg[j].addr << 1); memcpy(obuf + 2, msg[j].buf, msg[j].len); - ret = dw210x_op_rw(d->udev, 0x80, 0, 0, + dw210x_op_rw(d->udev, 0x80, 0, 0, obuf, msg[j].len + 2, DW210X_WRITE_MSG); break; diff --git a/drivers/media/dvb/dvb-usb/it913x.c b/drivers/media/dvb/dvb-usb/it913x.c index 482d249ca7f3..6244fe9d1a3a 100644 --- a/drivers/media/dvb/dvb-usb/it913x.c +++ b/drivers/media/dvb/dvb-usb/it913x.c @@ -81,7 +81,7 @@ static int it913x_bulk_write(struct usb_device *dev, for (i = 0; i < IT913X_RETRY; i++) { ret = usb_bulk_msg(dev, usb_sndbulkpipe(dev, pipe), snd, len , &actual_l, IT913X_SND_TIMEOUT); - if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT) + if (ret != -EBUSY && ret != -ETIMEDOUT) break; } @@ -99,7 +99,7 @@ static int it913x_bulk_read(struct usb_device *dev, for (i = 0; i < IT913X_RETRY; i++) { ret = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, pipe), rev, len , &actual_l, IT913X_RCV_TIMEOUT); - if (ret == 0 || ret != -EBUSY || ret != -ETIMEDOUT) + if (ret != -EBUSY && ret != -ETIMEDOUT) break; } diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index 5dde06d066ff..25d1031460f8 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -373,7 +373,7 @@ static int lme2510_pid_filter_ctrl(struct dvb_usb_adapter *adap, int onoff) struct lme2510_state *st = adap->dev->priv; static u8 clear_pid_reg[] = LME_ALL_PIDS; static u8 rbuf[1]; - int ret; + int ret = 0; deb_info(1, "PID Clearing Filter"); @@ -1205,14 +1205,13 @@ static int lme2510_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct usb_device *udev = interface_to_usbdev(intf); - int ret = 0; usb_reset_configuration(udev); usb_set_interface(udev, intf->cur_altsetting->desc.bInterfaceNumber, 1); if (udev->speed != USB_SPEED_HIGH) { - ret = usb_reset_device(udev); + usb_reset_device(udev); info("DEV Failed to connect in HIGH SPEED mode"); return -ENODEV; } diff --git a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c index 72db6eef4b9c..74da5bb1ce99 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c +++ b/drivers/media/dvb/dvb-usb/mxl111sf-tuner.c @@ -284,6 +284,7 @@ static int mxl111sf_tuner_set_params(struct dvb_frontend *fe) switch (delsys) { case SYS_ATSC: + case SYS_ATSCMH: bw = 0; /* ATSC */ break; case SYS_DVBC_ANNEX_B: diff --git a/drivers/media/dvb/dvb-usb/mxl111sf.c b/drivers/media/dvb/dvb-usb/mxl111sf.c index 81305de2fea5..cd842798f5af 100644 --- a/drivers/media/dvb/dvb-usb/mxl111sf.c +++ b/drivers/media/dvb/dvb-usb/mxl111sf.c @@ -21,6 +21,7 @@ #include "mxl111sf-tuner.h" #include "lgdt3305.h" +#include "lg2160.h" int dvb_usb_mxl111sf_debug; module_param_named(debug, dvb_usb_mxl111sf_debug, int, 0644); @@ -31,6 +32,10 @@ int dvb_usb_mxl111sf_isoc; module_param_named(isoc, dvb_usb_mxl111sf_isoc, int, 0644); MODULE_PARM_DESC(isoc, "enable usb isoc xfer (0=bulk, 1=isoc)."); +int dvb_usb_mxl111sf_spi; +module_param_named(spi, dvb_usb_mxl111sf_spi, int, 0644); +MODULE_PARM_DESC(spi, "use spi rather than tp for data xfer (0=tp, 1=spi)."); + #define ANT_PATH_AUTO 0 #define ANT_PATH_EXTERNAL 1 #define ANT_PATH_INTERNAL 2 @@ -340,7 +345,6 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) struct mxl111sf_state *state = d->priv; struct mxl111sf_adap_state *adap_state = adap->fe_adap[adap->active_fe].priv; int ret = 0; - u8 tmp; deb_info("%s(%d)\n", __func__, onoff); @@ -361,6 +365,33 @@ static int mxl111sf_ep6_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) return ret; } +static int mxl111sf_ep5_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int ret = 0; + + deb_info("%s(%d)\n", __func__, onoff); + + if (onoff) { + ret = mxl111sf_enable_usb_output(state); + mxl_fail(ret); + + ret = mxl111sf_init_i2s_port(state, 200); + mxl_fail(ret); + ret = mxl111sf_config_i2s(state, 0, 15); + mxl_fail(ret); + } else { + ret = mxl111sf_disable_i2s_port(state); + mxl_fail(ret); + } + if (state->chip_rev > MXL111SF_V6) + ret = mxl111sf_config_spi(state, onoff); + mxl_fail(ret); + + return ret; +} + static int mxl111sf_ep4_streaming_ctrl(struct dvb_usb_adapter *adap, int onoff) { struct dvb_usb_device *d = adap->dev; @@ -453,6 +484,255 @@ fail: return ret; } +static struct lg2160_config hauppauge_lg2160_config = { + .lg_chip = LG2160, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, +}; + +static int mxl111sf_lg2160_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach, + &hauppauge_lg2160_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct lg2160_config hauppauge_lg2161_1019_config = { + .lg_chip = LG2161_1019, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 2, /* LG2161_OIF_SPI_MAS */ +}; + +static struct lg2160_config hauppauge_lg2161_1040_config = { + .lg_chip = LG2161_1040, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 4, /* LG2161_OIF_SPI_MAS */ +}; + +static int mxl111sf_lg2161_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 1; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach, + (MXL111SF_V8_200 == state->chip_rev) ? + &hauppauge_lg2161_1040_config : + &hauppauge_lg2161_1019_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + +static struct lg2160_config hauppauge_lg2161_1019_ep6_config = { + .lg_chip = LG2161_1019, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 1, /* LG2161_OIF_SERIAL_TS */ +}; + +static struct lg2160_config hauppauge_lg2161_1040_ep6_config = { + .lg_chip = LG2161_1040, + .i2c_addr = 0x1c >> 1, + .deny_i2c_rptr = 1, + .spectral_inversion = 0, + .if_khz = 6000, + .output_if = 7, /* LG2161_OIF_SERIAL_TS */ +}; + +static int mxl111sf_lg2161_ep6_frontend_attach(struct dvb_usb_adapter *adap) +{ + struct dvb_usb_device *d = adap->dev; + struct mxl111sf_state *state = d->priv; + int fe_id = adap->num_frontends_initialized; + struct mxl111sf_adap_state *adap_state = adap->fe_adap[fe_id].priv; + int ret; + + deb_adv("%s()\n", __func__); + + /* save a pointer to the dvb_usb_device in device state */ + state->d = d; + adap_state->alt_mode = (dvb_usb_mxl111sf_isoc) ? 2 : 1; + state->alt_mode = adap_state->alt_mode; + + if (usb_set_interface(adap->dev->udev, 0, state->alt_mode) < 0) + err("set interface failed"); + + state->gpio_mode = MXL111SF_GPIO_MOD_MH; + adap_state->gpio_mode = state->gpio_mode; + adap_state->device_mode = MXL_TUNER_MODE; + adap_state->ep6_clockphase = 0; + + ret = mxl1x1sf_soft_reset(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_init_tuner_demod(state); + if (mxl_fail(ret)) + goto fail; + + ret = mxl1x1sf_set_device_mode(state, adap_state->device_mode); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_enable_usb_output(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl1x1sf_top_master_ctrl(state, 1); + if (mxl_fail(ret)) + goto fail; + + ret = mxl111sf_init_port_expander(state); + if (mxl_fail(ret)) + goto fail; + ret = mxl111sf_gpio_mode_switch(state, state->gpio_mode); + if (mxl_fail(ret)) + goto fail; + + ret = get_chip_info(state); + if (mxl_fail(ret)) + goto fail; + + adap->fe_adap[fe_id].fe = dvb_attach(lg2160_attach, + (MXL111SF_V8_200 == state->chip_rev) ? + &hauppauge_lg2161_1040_ep6_config : + &hauppauge_lg2161_1019_ep6_config, + &adap->dev->i2c_adap); + if (adap->fe_adap[fe_id].fe) { + adap_state->fe_init = adap->fe_adap[fe_id].fe->ops.init; + adap->fe_adap[fe_id].fe->ops.init = mxl111sf_adap_fe_init; + adap_state->fe_sleep = adap->fe_adap[fe_id].fe->ops.sleep; + adap->fe_adap[fe_id].fe->ops.sleep = mxl111sf_adap_fe_sleep; + return 0; + } + ret = -EIO; +fail: + return ret; +} + static struct mxl111sf_demod_config mxl_demod_config = { .read_reg = mxl111sf_read_reg, .write_reg = mxl111sf_write_reg, @@ -650,6 +930,18 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties; static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties; static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties; static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_atsc_mh_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_atsc_mh_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mh_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mh_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_spi_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_spi_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_tp_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_tp_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_isoc_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_bulk_properties; +static struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_isoc_properties; static int mxl111sf_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -664,12 +956,50 @@ static int mxl111sf_probe(struct usb_interface *intf, THIS_MODULE, &d, adapter_nr) || 0 == dvb_usb_device_init(intf, &mxl111sf_atsc_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_atsc_mh_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mh_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + ((dvb_usb_mxl111sf_spi) && + (0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_spi_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_mh_spi_isoc_properties, + THIS_MODULE, &d, adapter_nr))) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_tp_isoc_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_mh_tp_isoc_properties, THIS_MODULE, &d, adapter_nr))) || 0 == dvb_usb_device_init(intf, &mxl111sf_dvbt_bulk_properties, THIS_MODULE, &d, adapter_nr) || 0 == dvb_usb_device_init(intf, &mxl111sf_atsc_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_atsc_mh_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mh_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + ((dvb_usb_mxl111sf_spi) && + (0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_spi_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_mh_spi_bulk_properties, + THIS_MODULE, &d, adapter_nr))) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_tp_bulk_properties, + THIS_MODULE, &d, adapter_nr) || + 0 == dvb_usb_device_init(intf, + &mxl111sf_mercury_mh_tp_bulk_properties, THIS_MODULE, &d, adapter_nr) || 0) { struct mxl111sf_state *state = d->priv; @@ -787,6 +1117,36 @@ MODULE_DEVICE_TABLE(usb, mxl111sf_table); } \ } +#define MXL111SF_EP5_BULK_STREAMING_CONFIG \ + .size_of_priv = sizeof(struct mxl111sf_adap_state), \ + .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, \ + .stream = { \ + .type = USB_BULK, \ + .count = 5, \ + .endpoint = 0x05, \ + .u = { \ + .bulk = { \ + .buffersize = 8192, \ + } \ + } \ + } + +#define MXL111SF_EP5_ISOC_STREAMING_CONFIG \ + .size_of_priv = sizeof(struct mxl111sf_adap_state), \ + .streaming_ctrl = mxl111sf_ep5_streaming_ctrl, \ + .stream = { \ + .type = USB_ISOC, \ + .count = 5, \ + .endpoint = 0x05, \ + .u = { \ + .isoc = { \ + .framesperurb = 96, \ + .framesize = 200, \ + .interval = 1, \ + } \ + } \ + } + #define MXL111SF_EP6_BULK_STREAMING_CONFIG \ .size_of_priv = sizeof(struct mxl111sf_adap_state), \ .streaming_ctrl = mxl111sf_ep6_streaming_ctrl, \ @@ -848,7 +1208,7 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = { } }, }, }, - .num_device_descs = 4, + .num_device_descs = 3, .devices = { { "Hauppauge 126xxx DVBT (bulk)", { NULL }, @@ -866,11 +1226,6 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_bulk_properties = { &mxl111sf_table[24], &mxl111sf_table[26], NULL }, }, - { "Hauppauge 126xxx (tp-bulk)", - { NULL }, - { &mxl111sf_table[28], &mxl111sf_table[30], - NULL }, - }, } }; @@ -890,7 +1245,7 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = { } }, }, }, - .num_device_descs = 4, + .num_device_descs = 3, .devices = { { "Hauppauge 126xxx DVBT (isoc)", { NULL }, @@ -908,11 +1263,6 @@ static struct dvb_usb_device_properties mxl111sf_dvbt_isoc_properties = { &mxl111sf_table[24], &mxl111sf_table[26], NULL }, }, - { "Hauppauge 126xxx (tp-isoc)", - { NULL }, - { &mxl111sf_table[28], &mxl111sf_table[30], - NULL }, - }, } }; @@ -923,22 +1273,16 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { .adapter = { { .fe_ioctl_override = mxl111sf_fe_ioctl_override, - .num_frontends = 2, + .num_frontends = 1, .fe = {{ .frontend_attach = mxl111sf_lgdt3305_frontend_attach, .tuner_attach = mxl111sf_attach_tuner, MXL111SF_EP6_BULK_STREAMING_CONFIG, - }, - { - .frontend_attach = mxl111sf_attach_demod, - .tuner_attach = mxl111sf_attach_tuner, - - MXL111SF_EP4_BULK_STREAMING_CONFIG, }}, }, }, - .num_device_descs = 6, + .num_device_descs = 2, .devices = { { "Hauppauge 126xxx ATSC (bulk)", { NULL }, @@ -950,6 +1294,138 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { { &mxl111sf_table[12], NULL }, }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "Hauppauge 126xxx ATSC (isoc)", + { NULL }, + { &mxl111sf_table[1], &mxl111sf_table[5], + NULL }, + }, + { "Hauppauge 117xxx ATSC (isoc)", + { NULL }, + { &mxl111sf_table[12], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mh_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2160_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "HCW 126xxx (bulk)", + { NULL }, + { &mxl111sf_table[2], &mxl111sf_table[6], + NULL }, + }, + { "HCW 117xxx (bulk)", + { NULL }, + { &mxl111sf_table[13], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mh_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 1, + .fe = {{ + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2160_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "HCW 126xxx (isoc)", + { NULL }, + { &mxl111sf_table[2], &mxl111sf_table[6], + NULL }, + }, + { "HCW 117xxx (isoc)", + { NULL }, + { &mxl111sf_table[13], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_mh_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2160_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { { "Hauppauge 126xxx ATSC+ (bulk)", { NULL }, { &mxl111sf_table[0], &mxl111sf_table[3], @@ -963,6 +1439,181 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { &mxl111sf_table[32], &mxl111sf_table[33], NULL }, }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_atsc_mh_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2160_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "Hauppauge 126xxx ATSC+ (isoc)", + { NULL }, + { &mxl111sf_table[0], &mxl111sf_table[3], + &mxl111sf_table[7], &mxl111sf_table[9], + &mxl111sf_table[10], NULL }, + }, + { "Hauppauge 117xxx ATSC+ (isoc)", + { NULL }, + { &mxl111sf_table[11], &mxl111sf_table[14], + &mxl111sf_table[16], &mxl111sf_table[17], + &mxl111sf_table[32], &mxl111sf_table[33], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mercury_spi_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "Hauppauge Mercury (spi-bulk)", + { NULL }, + { &mxl111sf_table[19], &mxl111sf_table[21], + &mxl111sf_table[23], &mxl111sf_table[25], + NULL }, + }, + { "Hauppauge WinTV-Aero-M (spi-bulk)", + { NULL }, + { &mxl111sf_table[29], &mxl111sf_table[31], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mercury_spi_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { + { "Hauppauge Mercury (spi-isoc)", + { NULL }, + { &mxl111sf_table[19], &mxl111sf_table[21], + &mxl111sf_table[23], &mxl111sf_table[25], + NULL }, + }, + { "Hauppauge WinTV-Aero-M (spi-isoc)", + { NULL }, + { &mxl111sf_table[29], &mxl111sf_table[31], + NULL }, + }, + } +}; + +static struct dvb_usb_device_properties mxl111sf_mercury_tp_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 3, + .fe = {{ + .frontend_attach = mxl111sf_lgdt3305_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }, + { + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 2, + .devices = { { "Hauppauge Mercury (tp-bulk)", { NULL }, { &mxl111sf_table[19], &mxl111sf_table[21], @@ -977,14 +1628,14 @@ static struct dvb_usb_device_properties mxl111sf_atsc_bulk_properties = { } }; -static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { +static struct dvb_usb_device_properties mxl111sf_mercury_tp_isoc_properties = { MXL111SF_DEFAULT_DEVICE_PROPERTIES, .num_adapters = 1, .adapter = { { .fe_ioctl_override = mxl111sf_fe_ioctl_override, - .num_frontends = 2, + .num_frontends = 3, .fe = {{ .frontend_attach = mxl111sf_lgdt3305_frontend_attach, .tuner_attach = mxl111sf_attach_tuner, @@ -996,34 +1647,19 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { .tuner_attach = mxl111sf_attach_tuner, MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, }}, }, }, - .num_device_descs = 6, + .num_device_descs = 2, .devices = { - { "Hauppauge 126xxx ATSC (isoc)", - { NULL }, - { &mxl111sf_table[1], &mxl111sf_table[5], - NULL }, - }, - { "Hauppauge 117xxx ATSC (isoc)", - { NULL }, - { &mxl111sf_table[12], - NULL }, - }, - { "Hauppauge 126xxx ATSC+ (isoc)", - { NULL }, - { &mxl111sf_table[0], &mxl111sf_table[3], - &mxl111sf_table[7], &mxl111sf_table[9], - &mxl111sf_table[10], NULL }, - }, - { "Hauppauge 117xxx ATSC+ (isoc)", - { NULL }, - { &mxl111sf_table[11], &mxl111sf_table[14], - &mxl111sf_table[16], &mxl111sf_table[17], - &mxl111sf_table[32], &mxl111sf_table[33], - NULL }, - }, { "Hauppauge Mercury (tp-isoc)", { NULL }, { &mxl111sf_table[19], &mxl111sf_table[21], @@ -1038,6 +1674,146 @@ static struct dvb_usb_device_properties mxl111sf_atsc_isoc_properties = { } }; +static +struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 1, + .devices = { + { "Hauppauge 126xxx (tp-bulk)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static +struct dvb_usb_device_properties mxl111sf_mercury_mh_tp_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_ep6_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP6_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 1, + .devices = { + { "Hauppauge 126xxx (tp-isoc)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static +struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_bulk_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_BULK_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_BULK_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 1, + .devices = { + { "Hauppauge 126xxx (spi-bulk)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + +static +struct dvb_usb_device_properties mxl111sf_mercury_mh_spi_isoc_properties = { + MXL111SF_DEFAULT_DEVICE_PROPERTIES, + + .num_adapters = 1, + .adapter = { + { + .fe_ioctl_override = mxl111sf_fe_ioctl_override, + .num_frontends = 2, + .fe = {{ + .frontend_attach = mxl111sf_attach_demod, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP4_ISOC_STREAMING_CONFIG, + }, + { + .caps = DVB_USB_ADAP_RECEIVES_RAW_PAYLOAD, + + .frontend_attach = mxl111sf_lg2161_frontend_attach, + .tuner_attach = mxl111sf_attach_tuner, + + MXL111SF_EP5_ISOC_STREAMING_CONFIG, + }}, + }, + }, + .num_device_descs = 1, + .devices = { + { "Hauppauge 126xxx (spi-isoc)", + { NULL }, + { &mxl111sf_table[28], &mxl111sf_table[30], + NULL }, + }, + } +}; + static struct usb_driver mxl111sf_driver = { .name = "dvb_usb_mxl111sf", .probe = mxl111sf_probe, diff --git a/drivers/media/dvb/dvb-usb/rtl28xxu.c b/drivers/media/dvb/dvb-usb/rtl28xxu.c index 8f4736a10fc8..41e1f5537f44 100644 --- a/drivers/media/dvb/dvb-usb/rtl28xxu.c +++ b/drivers/media/dvb/dvb-usb/rtl28xxu.c @@ -322,6 +322,9 @@ static int rtl2831u_frontend_attach(struct dvb_usb_adapter *adap) * since there is some demod params needed to set according to tuner. */ + /* demod needs some time to wake up */ + msleep(20); + /* open demod I2C gate */ ret = rtl28xxu_ctrl_msg(adap->dev, &req_gate); if (ret) @@ -909,6 +912,8 @@ static int rtl28xxu_probe(struct usb_interface *intf, int ret, i; int properties_count = ARRAY_SIZE(rtl28xxu_properties); struct dvb_usb_device *d; + struct usb_device *udev; + bool found; deb_info("%s: interface=%d\n", __func__, intf->cur_altsetting->desc.bInterfaceNumber); @@ -916,6 +921,29 @@ static int rtl28xxu_probe(struct usb_interface *intf, if (intf->cur_altsetting->desc.bInterfaceNumber != 0) return 0; + /* Dynamic USB ID support. Replaces first device ID with current one .*/ + udev = interface_to_usbdev(intf); + + for (i = 0, found = false; i < ARRAY_SIZE(rtl28xxu_table) - 1; i++) { + if (rtl28xxu_table[i].idVendor == + le16_to_cpu(udev->descriptor.idVendor) && + rtl28xxu_table[i].idProduct == + le16_to_cpu(udev->descriptor.idProduct)) { + found = true; + break; + } + } + + if (!found) { + deb_info("%s: using dynamic ID %04x:%04x\n", __func__, + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + rtl28xxu_properties[0].devices[0].warm_ids[0]->idVendor = + le16_to_cpu(udev->descriptor.idVendor); + rtl28xxu_properties[0].devices[0].warm_ids[0]->idProduct = + le16_to_cpu(udev->descriptor.idProduct); + } + for (i = 0; i < properties_count; i++) { ret = dvb_usb_device_init(intf, &rtl28xxu_properties[i], THIS_MODULE, &d, adapter_nr); diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig index 21246707fbfb..b98ebb264e29 100644 --- a/drivers/media/dvb/frontends/Kconfig +++ b/drivers/media/dvb/frontends/Kconfig @@ -531,6 +531,14 @@ config DVB_LGDT3305 An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want to support this frontend. +config DVB_LG2160 + tristate "LG Electronics LG216x based" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + help + An ATSC/MH demodulator module. Say Y when you want + to support this frontend. + config DVB_S5H1409 tristate "Samsung S5H1409 based" depends on DVB_CORE && I2C @@ -540,12 +548,26 @@ config DVB_S5H1409 to support this frontend. config DVB_AU8522 - tristate "Auvitek AU8522 based" - depends on DVB_CORE && I2C && VIDEO_V4L2 + depends on I2C + tristate + +config DVB_AU8522_DTV + tristate "Auvitek AU8522 based DTV demod" + depends on DVB_CORE && I2C + select DVB_AU8522 default m if DVB_FE_CUSTOMISE help - An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want - to support this frontend. + An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when + you want to enable DTV demodulation support for this frontend. + +config DVB_AU8522_V4L + tristate "Auvitek AU8522 based ATV demod" + depends on VIDEO_V4L2 && I2C + select DVB_AU8522 + default m if DVB_FE_CUSTOMISE + help + An ATSC 8VSB, QAM64/256 & NTSC demodulator module. Say Y when + you want to enable ATV demodulation support for this frontend. config DVB_S5H1411 tristate "Samsung S5H1411 based" @@ -713,6 +735,11 @@ config DVB_M88RS2000 A DVB-S tuner module. Say Y when you want to support this frontend. +config DVB_AF9033 + tristate "Afatech AF9033 DVB-T demodulator" + depends on DVB_CORE && I2C + default m if DVB_FE_CUSTOMISE + 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 86fa808bf589..cd1ac2fd5774 100644 --- a/drivers/media/dvb/frontends/Makefile +++ b/drivers/media/dvb/frontends/Makefile @@ -7,7 +7,6 @@ ccflags-y += -I$(srctree)/drivers/media/common/tuners/ stb0899-objs = stb0899_drv.o stb0899_algo.o stv0900-objs = stv0900_core.o stv0900_sw.o -au8522-objs = au8522_dig.o au8522_decoder.o drxd-objs = drxd_firm.o drxd_hard.o cxd2820r-objs = cxd2820r_core.o cxd2820r_c.o cxd2820r_t.o cxd2820r_t2.o drxk-objs := drxk_hard.o @@ -50,6 +49,7 @@ obj-$(CONFIG_DVB_BCM3510) += bcm3510.o obj-$(CONFIG_DVB_S5H1420) += s5h1420.o obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o +obj-$(CONFIG_DVB_LG2160) += lg2160.o obj-$(CONFIG_DVB_CX24123) += cx24123.o obj-$(CONFIG_DVB_LNBP21) += lnbp21.o obj-$(CONFIG_DVB_LNBP22) += lnbp22.o @@ -63,7 +63,9 @@ obj-$(CONFIG_DVB_TUNER_DIB0090) += dib0090.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o obj-$(CONFIG_DVB_S5H1409) += s5h1409.o obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o -obj-$(CONFIG_DVB_AU8522) += au8522.o +obj-$(CONFIG_DVB_AU8522) += au8522_common.o +obj-$(CONFIG_DVB_AU8522_DTV) += au8522_dig.o +obj-$(CONFIG_DVB_AU8522_V4L) += au8522_decoder.o obj-$(CONFIG_DVB_TDA10048) += tda10048.o obj-$(CONFIG_DVB_TUNER_CX24113) += cx24113.o obj-$(CONFIG_DVB_S5H1411) += s5h1411.o @@ -98,4 +100,5 @@ obj-$(CONFIG_DVB_A8293) += a8293.o obj-$(CONFIG_DVB_TDA10071) += tda10071.o obj-$(CONFIG_DVB_RTL2830) += rtl2830.o obj-$(CONFIG_DVB_M88RS2000) += m88rs2000.o +obj-$(CONFIG_DVB_AF9033) += af9033.o diff --git a/drivers/media/dvb/frontends/af9013.c b/drivers/media/dvb/frontends/af9013.c index 6bcbcf543b38..5bc570d77846 100644 --- a/drivers/media/dvb/frontends/af9013.c +++ b/drivers/media/dvb/frontends/af9013.c @@ -514,7 +514,6 @@ err: static void af9013_statistics_work(struct work_struct *work) { - int ret; struct af9013_state *state = container_of(work, struct af9013_state, statistics_work.work); unsigned int next_msec; @@ -530,27 +529,27 @@ static void af9013_statistics_work(struct work_struct *work) default: state->statistics_step = 0; case 0: - ret = af9013_statistics_signal_strength(&state->fe); + af9013_statistics_signal_strength(&state->fe); state->statistics_step++; next_msec = 300; break; case 1: - ret = af9013_statistics_snr_start(&state->fe); + af9013_statistics_snr_start(&state->fe); state->statistics_step++; next_msec = 200; break; case 2: - ret = af9013_statistics_ber_unc_start(&state->fe); + af9013_statistics_ber_unc_start(&state->fe); state->statistics_step++; next_msec = 1000; break; case 3: - ret = af9013_statistics_snr_result(&state->fe); + af9013_statistics_snr_result(&state->fe); state->statistics_step++; next_msec = 400; break; case 4: - ret = af9013_statistics_ber_unc_result(&state->fe); + af9013_statistics_ber_unc_result(&state->fe); state->statistics_step++; next_msec = 100; break; @@ -558,8 +557,6 @@ static void af9013_statistics_work(struct work_struct *work) schedule_delayed_work(&state->statistics_work, msecs_to_jiffies(next_msec)); - - return; } static int af9013_get_tune_settings(struct dvb_frontend *fe, diff --git a/drivers/media/dvb/frontends/af9033.c b/drivers/media/dvb/frontends/af9033.c new file mode 100644 index 000000000000..a38998286260 --- /dev/null +++ b/drivers/media/dvb/frontends/af9033.c @@ -0,0 +1,980 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "af9033_priv.h" + +struct af9033_state { + struct i2c_adapter *i2c; + struct dvb_frontend fe; + struct af9033_config cfg; + + u32 bandwidth_hz; + bool ts_mode_parallel; + bool ts_mode_serial; + + u32 ber; + u32 ucb; + unsigned long last_stat_check; +}; + +/* write multiple registers */ +static int af9033_wr_regs(struct af9033_state *state, u32 reg, const u8 *val, + int len) +{ + int ret; + u8 buf[3 + len]; + struct i2c_msg msg[1] = { + { + .addr = state->cfg.i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + } + }; + + buf[0] = (reg >> 16) & 0xff; + buf[1] = (reg >> 8) & 0xff; + buf[2] = (reg >> 0) & 0xff; + memcpy(&buf[3], val, len); + + ret = i2c_transfer(state->i2c, msg, 1); + if (ret == 1) { + ret = 0; + } else { + printk(KERN_WARNING "%s: i2c wr failed=%d reg=%06x len=%d\n", + __func__, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + +/* read multiple registers */ +static int af9033_rd_regs(struct af9033_state *state, u32 reg, u8 *val, int len) +{ + int ret; + u8 buf[3] = { (reg >> 16) & 0xff, (reg >> 8) & 0xff, + (reg >> 0) & 0xff }; + struct i2c_msg msg[2] = { + { + .addr = state->cfg.i2c_addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf + }, { + .addr = state->cfg.i2c_addr, + .flags = I2C_M_RD, + .len = len, + .buf = val + } + }; + + ret = i2c_transfer(state->i2c, msg, 2); + if (ret == 2) { + ret = 0; + } else { + printk(KERN_WARNING "%s: i2c rd failed=%d reg=%06x len=%d\n", + __func__, ret, reg, len); + ret = -EREMOTEIO; + } + + return ret; +} + + +/* write single register */ +static int af9033_wr_reg(struct af9033_state *state, u32 reg, u8 val) +{ + return af9033_wr_regs(state, reg, &val, 1); +} + +/* read single register */ +static int af9033_rd_reg(struct af9033_state *state, u32 reg, u8 *val) +{ + return af9033_rd_regs(state, reg, val, 1); +} + +/* write single register with mask */ +static int af9033_wr_reg_mask(struct af9033_state *state, u32 reg, u8 val, + u8 mask) +{ + int ret; + u8 tmp; + + /* no need for read if whole reg is written */ + if (mask != 0xff) { + ret = af9033_rd_regs(state, reg, &tmp, 1); + if (ret) + return ret; + + val &= mask; + tmp &= ~mask; + val |= tmp; + } + + return af9033_wr_regs(state, reg, &val, 1); +} + +/* read single register with mask */ +static int af9033_rd_reg_mask(struct af9033_state *state, u32 reg, u8 *val, + u8 mask) +{ + int ret, i; + u8 tmp; + + ret = af9033_rd_regs(state, reg, &tmp, 1); + if (ret) + return ret; + + tmp &= mask; + + /* find position of the first bit */ + for (i = 0; i < 8; i++) { + if ((mask >> i) & 0x01) + break; + } + *val = tmp >> i; + + return 0; +} + +static u32 af9033_div(u32 a, u32 b, u32 x) +{ + u32 r = 0, c = 0, i; + + pr_debug("%s: a=%d b=%d x=%d\n", __func__, a, b, x); + + if (a > b) { + c = a / b; + a = a - c * b; + } + + for (i = 0; i < x; i++) { + if (a >= b) { + r += 1; + a -= b; + } + a <<= 1; + r <<= 1; + } + r = (c << (u32)x) + r; + + pr_debug("%s: a=%d b=%d x=%d r=%d r=%x\n", __func__, a, b, x, r, r); + + return r; +} + +static void af9033_release(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + + kfree(state); +} + +static int af9033_init(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret, i, len; + const struct reg_val *init; + u8 buf[4]; + u32 adc_cw, clock_cw; + struct reg_val_mask tab[] = { + { 0x80fb24, 0x00, 0x08 }, + { 0x80004c, 0x00, 0xff }, + { 0x00f641, state->cfg.tuner, 0xff }, + { 0x80f5ca, 0x01, 0x01 }, + { 0x80f715, 0x01, 0x01 }, + { 0x00f41f, 0x04, 0x04 }, + { 0x00f41a, 0x01, 0x01 }, + { 0x80f731, 0x00, 0x01 }, + { 0x00d91e, 0x00, 0x01 }, + { 0x00d919, 0x00, 0x01 }, + { 0x80f732, 0x00, 0x01 }, + { 0x00d91f, 0x00, 0x01 }, + { 0x00d91a, 0x00, 0x01 }, + { 0x80f730, 0x00, 0x01 }, + { 0x80f778, 0x00, 0xff }, + { 0x80f73c, 0x01, 0x01 }, + { 0x80f776, 0x00, 0x01 }, + { 0x00d8fd, 0x01, 0xff }, + { 0x00d830, 0x01, 0xff }, + { 0x00d831, 0x00, 0xff }, + { 0x00d832, 0x00, 0xff }, + { 0x80f985, state->ts_mode_serial, 0x01 }, + { 0x80f986, state->ts_mode_parallel, 0x01 }, + { 0x00d827, 0x00, 0xff }, + { 0x00d829, 0x00, 0xff }, + }; + + /* program clock control */ + clock_cw = af9033_div(state->cfg.clock, 1000000ul, 19ul); + buf[0] = (clock_cw >> 0) & 0xff; + buf[1] = (clock_cw >> 8) & 0xff; + buf[2] = (clock_cw >> 16) & 0xff; + buf[3] = (clock_cw >> 24) & 0xff; + + pr_debug("%s: clock=%d clock_cw=%08x\n", __func__, state->cfg.clock, + clock_cw); + + ret = af9033_wr_regs(state, 0x800025, buf, 4); + if (ret < 0) + goto err; + + /* program ADC control */ + for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { + if (clock_adc_lut[i].clock == state->cfg.clock) + break; + } + + adc_cw = af9033_div(clock_adc_lut[i].adc, 1000000ul, 19ul); + buf[0] = (adc_cw >> 0) & 0xff; + buf[1] = (adc_cw >> 8) & 0xff; + buf[2] = (adc_cw >> 16) & 0xff; + + pr_debug("%s: adc=%d adc_cw=%06x\n", __func__, clock_adc_lut[i].adc, + adc_cw); + + ret = af9033_wr_regs(state, 0x80f1cd, buf, 3); + if (ret < 0) + goto err; + + /* program register table */ + for (i = 0; i < ARRAY_SIZE(tab); i++) { + ret = af9033_wr_reg_mask(state, tab[i].reg, tab[i].val, + tab[i].mask); + if (ret < 0) + goto err; + } + + /* settings for TS interface */ + if (state->cfg.ts_mode == AF9033_TS_MODE_USB) { + ret = af9033_wr_reg_mask(state, 0x80f9a5, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x01, 0x01); + if (ret < 0) + goto err; + } else { + ret = af9033_wr_reg_mask(state, 0x80f990, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f9b5, 0x00, 0x01); + if (ret < 0) + goto err; + } + + /* load OFSM settings */ + pr_debug("%s: load ofsm settings\n", __func__); + len = ARRAY_SIZE(ofsm_init); + init = ofsm_init; + for (i = 0; i < len; i++) { + ret = af9033_wr_reg(state, init[i].reg, init[i].val); + if (ret < 0) + goto err; + } + + /* load tuner specific settings */ + pr_debug("%s: load tuner specific settings\n", + __func__); + switch (state->cfg.tuner) { + case AF9033_TUNER_TUA9001: + len = ARRAY_SIZE(tuner_init_tua9001); + init = tuner_init_tua9001; + break; + case AF9033_TUNER_FC0011: + len = ARRAY_SIZE(tuner_init_fc0011); + init = tuner_init_fc0011; + break; + case AF9033_TUNER_MXL5007T: + len = ARRAY_SIZE(tuner_init_mxl5007t); + init = tuner_init_mxl5007t; + break; + case AF9033_TUNER_TDA18218: + len = ARRAY_SIZE(tuner_init_tda18218); + init = tuner_init_tda18218; + break; + default: + pr_debug("%s: unsupported tuner ID=%d\n", __func__, + state->cfg.tuner); + ret = -ENODEV; + goto err; + } + + for (i = 0; i < len; i++) { + ret = af9033_wr_reg(state, init[i].reg, init[i].val); + if (ret < 0) + goto err; + } + + state->bandwidth_hz = 0; /* force to program all parameters */ + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_sleep(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret, i; + u8 tmp; + + ret = af9033_wr_reg(state, 0x80004c, 1); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800000, 0); + if (ret < 0) + goto err; + + for (i = 100, tmp = 1; i && tmp; i--) { + ret = af9033_rd_reg(state, 0x80004c, &tmp); + if (ret < 0) + goto err; + + usleep_range(200, 10000); + } + + pr_debug("%s: loop=%d\n", __func__, i); + + if (i == 0) { + ret = -ETIMEDOUT; + goto err; + } + + ret = af9033_wr_reg_mask(state, 0x80fb24, 0x08, 0x08); + if (ret < 0) + goto err; + + /* prevent current leak (?) */ + if (state->cfg.ts_mode == AF9033_TS_MODE_SERIAL) { + /* enable parallel TS */ + ret = af9033_wr_reg_mask(state, 0x00d917, 0x00, 0x01); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x00d916, 0x01, 0x01); + if (ret < 0) + goto err; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings *fesettings) +{ + fesettings->min_delay_ms = 800; + fesettings->step_size = 0; + fesettings->max_drift = 0; + + return 0; +} + +static int af9033_set_frontend(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret, i, spec_inv; + u8 tmp, buf[3], bandwidth_reg_val; + u32 if_frequency, freq_cw, adc_freq; + + pr_debug("%s: frequency=%d bandwidth_hz=%d\n", __func__, c->frequency, + c->bandwidth_hz); + + /* check bandwidth */ + switch (c->bandwidth_hz) { + case 6000000: + bandwidth_reg_val = 0x00; + break; + case 7000000: + bandwidth_reg_val = 0x01; + break; + case 8000000: + bandwidth_reg_val = 0x02; + break; + default: + pr_debug("%s: invalid bandwidth_hz\n", __func__); + ret = -EINVAL; + goto err; + } + + /* program tuner */ + if (fe->ops.tuner_ops.set_params) + fe->ops.tuner_ops.set_params(fe); + + /* program CFOE coefficients */ + if (c->bandwidth_hz != state->bandwidth_hz) { + for (i = 0; i < ARRAY_SIZE(coeff_lut); i++) { + if (coeff_lut[i].clock == state->cfg.clock && + coeff_lut[i].bandwidth_hz == c->bandwidth_hz) { + break; + } + } + ret = af9033_wr_regs(state, 0x800001, + coeff_lut[i].val, sizeof(coeff_lut[i].val)); + } + + /* program frequency control */ + if (c->bandwidth_hz != state->bandwidth_hz) { + spec_inv = state->cfg.spec_inv ? -1 : 1; + + for (i = 0; i < ARRAY_SIZE(clock_adc_lut); i++) { + if (clock_adc_lut[i].clock == state->cfg.clock) + break; + } + adc_freq = clock_adc_lut[i].adc; + + /* get used IF frequency */ + if (fe->ops.tuner_ops.get_if_frequency) + fe->ops.tuner_ops.get_if_frequency(fe, &if_frequency); + else + if_frequency = 0; + + while (if_frequency > (adc_freq / 2)) + if_frequency -= adc_freq; + + if (if_frequency >= 0) + spec_inv *= -1; + else + if_frequency *= -1; + + freq_cw = af9033_div(if_frequency, adc_freq, 23ul); + + if (spec_inv == -1) + freq_cw *= -1; + + /* get adc multiplies */ + ret = af9033_rd_reg(state, 0x800045, &tmp); + if (ret < 0) + goto err; + + if (tmp == 1) + freq_cw /= 2; + + buf[0] = (freq_cw >> 0) & 0xff; + buf[1] = (freq_cw >> 8) & 0xff; + buf[2] = (freq_cw >> 16) & 0x7f; + ret = af9033_wr_regs(state, 0x800029, buf, 3); + if (ret < 0) + goto err; + + state->bandwidth_hz = c->bandwidth_hz; + } + + ret = af9033_wr_reg_mask(state, 0x80f904, bandwidth_reg_val, 0x03); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800040, 0x00); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800047, 0x00); + if (ret < 0) + goto err; + + ret = af9033_wr_reg_mask(state, 0x80f999, 0x00, 0x01); + if (ret < 0) + goto err; + + if (c->frequency <= 230000000) + tmp = 0x00; /* VHF */ + else + tmp = 0x01; /* UHF */ + + ret = af9033_wr_reg(state, 0x80004b, tmp); + if (ret < 0) + goto err; + + ret = af9033_wr_reg(state, 0x800000, 0x00); + if (ret < 0) + goto err; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_get_frontend(struct dvb_frontend *fe) +{ + struct af9033_state *state = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u8 buf[8]; + + pr_debug("%s\n", __func__); + + /* read all needed registers */ + ret = af9033_rd_regs(state, 0x80f900, buf, sizeof(buf)); + if (ret < 0) + goto err; + + switch ((buf[0] >> 0) & 3) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + break; + } + + switch ((buf[1] >> 0) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[2] >> 0) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[3] >> 0) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[4] >> 0) & 3) { + case 0: + c->bandwidth_hz = 6000000; + break; + case 1: + c->bandwidth_hz = 7000000; + break; + case 2: + c->bandwidth_hz = 8000000; + break; + } + + switch ((buf[6] >> 0) & 7) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + case 5: + c->code_rate_HP = FEC_NONE; + break; + } + + switch ((buf[7] >> 0) & 7) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + case 5: + c->code_rate_LP = FEC_NONE; + break; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + u8 tmp; + + *status = 0; + + /* radio channel status, 0=no result, 1=has signal, 2=no signal */ + ret = af9033_rd_reg(state, 0x800047, &tmp); + if (ret < 0) + goto err; + + /* has signal */ + if (tmp == 0x01) + *status |= FE_HAS_SIGNAL; + + if (tmp != 0x02) { + /* TPS lock */ + ret = af9033_rd_reg_mask(state, 0x80f5a9, &tmp, 0x01); + if (ret < 0) + goto err; + + if (tmp) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI; + + /* full lock */ + ret = af9033_rd_reg_mask(state, 0x80f999, &tmp, 0x01); + if (ret < 0) + goto err; + + if (tmp) + *status |= FE_HAS_SIGNAL | FE_HAS_CARRIER | + FE_HAS_VITERBI | FE_HAS_SYNC | + FE_HAS_LOCK; + } + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret, i, len; + u8 buf[3], tmp; + u32 snr_val; + const struct val_snr *uninitialized_var(snr_lut); + + /* read value */ + ret = af9033_rd_regs(state, 0x80002c, buf, 3); + if (ret < 0) + goto err; + + snr_val = (buf[2] << 16) | (buf[1] << 8) | buf[0]; + + /* read current modulation */ + ret = af9033_rd_reg(state, 0x80f903, &tmp); + if (ret < 0) + goto err; + + switch ((tmp >> 0) & 3) { + case 0: + len = ARRAY_SIZE(qpsk_snr_lut); + snr_lut = qpsk_snr_lut; + break; + case 1: + len = ARRAY_SIZE(qam16_snr_lut); + snr_lut = qam16_snr_lut; + break; + case 2: + len = ARRAY_SIZE(qam64_snr_lut); + snr_lut = qam64_snr_lut; + break; + default: + goto err; + } + + for (i = 0; i < len; i++) { + tmp = snr_lut[i].snr; + + if (snr_val < snr_lut[i].val) + break; + } + + *snr = tmp * 10; /* dB/10 */ + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_read_signal_strength(struct dvb_frontend *fe, u16 *strength) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + u8 strength2; + + /* read signal strength of 0-100 scale */ + ret = af9033_rd_reg(state, 0x800048, &strength2); + if (ret < 0) + goto err; + + /* scale value to 0x0000-0xffff */ + *strength = strength2 * 0xffff / 100; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static int af9033_update_ch_stat(struct af9033_state *state) +{ + int ret = 0; + u32 err_cnt, bit_cnt; + u16 abort_cnt; + u8 buf[7]; + + /* only update data every half second */ + if (time_after(jiffies, state->last_stat_check + msecs_to_jiffies(500))) { + ret = af9033_rd_regs(state, 0x800032, buf, sizeof(buf)); + if (ret < 0) + goto err; + /* in 8 byte packets? */ + abort_cnt = (buf[1] << 8) + buf[0]; + /* in bits */ + err_cnt = (buf[4] << 16) + (buf[3] << 8) + buf[2]; + /* in 8 byte packets? always(?) 0x2710 = 10000 */ + bit_cnt = (buf[6] << 8) + buf[5]; + + if (bit_cnt < abort_cnt) { + abort_cnt = 1000; + state->ber = 0xffffffff; + } else { + /* 8 byte packets, that have not been rejected already */ + bit_cnt -= (u32)abort_cnt; + if (bit_cnt == 0) { + state->ber = 0xffffffff; + } else { + err_cnt -= (u32)abort_cnt * 8 * 8; + bit_cnt *= 8 * 8; + state->ber = err_cnt * (0xffffffff / bit_cnt); + } + } + state->ucb += abort_cnt; + state->last_stat_check = jiffies; + } + + return 0; +err: + pr_debug("%s: failed=%d\n", __func__, ret); + return ret; +} + +static int af9033_read_ber(struct dvb_frontend *fe, u32 *ber) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + + ret = af9033_update_ch_stat(state); + if (ret < 0) + return ret; + + *ber = state->ber; + + return 0; +} + +static int af9033_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + + ret = af9033_update_ch_stat(state); + if (ret < 0) + return ret; + + *ucblocks = state->ucb; + + return 0; +} + +static int af9033_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct af9033_state *state = fe->demodulator_priv; + int ret; + + pr_debug("%s: enable=%d\n", __func__, enable); + + ret = af9033_wr_reg_mask(state, 0x00fa04, enable, 0x01); + if (ret < 0) + goto err; + + return 0; + +err: + pr_debug("%s: failed=%d\n", __func__, ret); + + return ret; +} + +static struct dvb_frontend_ops af9033_ops; + +struct dvb_frontend *af9033_attach(const struct af9033_config *config, + struct i2c_adapter *i2c) +{ + int ret; + struct af9033_state *state; + u8 buf[8]; + + pr_debug("%s:\n", __func__); + + /* allocate memory for the internal state */ + state = kzalloc(sizeof(struct af9033_state), GFP_KERNEL); + if (state == NULL) + goto err; + + /* setup the state */ + state->i2c = i2c; + memcpy(&state->cfg, config, sizeof(struct af9033_config)); + + if (state->cfg.clock != 12000000) { + printk(KERN_INFO "af9033: unsupported clock=%d, only " \ + "12000000 Hz is supported currently\n", + state->cfg.clock); + goto err; + } + + /* firmware version */ + ret = af9033_rd_regs(state, 0x0083e9, &buf[0], 4); + if (ret < 0) + goto err; + + ret = af9033_rd_regs(state, 0x804191, &buf[4], 4); + if (ret < 0) + goto err; + + printk(KERN_INFO "af9033: firmware version: LINK=%d.%d.%d.%d " \ + "OFDM=%d.%d.%d.%d\n", buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5], buf[6], buf[7]); + + /* configure internal TS mode */ + switch (state->cfg.ts_mode) { + case AF9033_TS_MODE_PARALLEL: + state->ts_mode_parallel = true; + break; + case AF9033_TS_MODE_SERIAL: + state->ts_mode_serial = true; + break; + case AF9033_TS_MODE_USB: + /* usb mode for AF9035 */ + default: + break; + } + + /* create dvb_frontend */ + memcpy(&state->fe.ops, &af9033_ops, sizeof(struct dvb_frontend_ops)); + state->fe.demodulator_priv = state; + + return &state->fe; + +err: + kfree(state); + return NULL; +} +EXPORT_SYMBOL(af9033_attach); + +static struct dvb_frontend_ops af9033_ops = { + .delsys = { SYS_DVBT }, + .info = { + .name = "Afatech AF9033 (DVB-T)", + .frequency_min = 174000000, + .frequency_max = 862000000, + .frequency_stepsize = 250000, + .frequency_tolerance = 0, + .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_RECOVER | + FE_CAN_MUTE_TS + }, + + .release = af9033_release, + + .init = af9033_init, + .sleep = af9033_sleep, + + .get_tune_settings = af9033_get_tune_settings, + .set_frontend = af9033_set_frontend, + .get_frontend = af9033_get_frontend, + + .read_status = af9033_read_status, + .read_snr = af9033_read_snr, + .read_signal_strength = af9033_read_signal_strength, + .read_ber = af9033_read_ber, + .read_ucblocks = af9033_read_ucblocks, + + .i2c_gate_ctrl = af9033_i2c_gate_ctrl, +}; + +MODULE_AUTHOR("Antti Palosaari "); +MODULE_DESCRIPTION("Afatech AF9033 DVB-T demodulator driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/dvb/frontends/af9033.h b/drivers/media/dvb/frontends/af9033.h new file mode 100644 index 000000000000..9e302c3f0f7d --- /dev/null +++ b/drivers/media/dvb/frontends/af9033.h @@ -0,0 +1,75 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9033_H +#define AF9033_H + +struct af9033_config { + /* + * I2C address + */ + u8 i2c_addr; + + /* + * clock Hz + * 12000000, 22000000, 24000000, 34000000, 32000000, 28000000, 26000000, + * 30000000, 36000000, 20480000, 16384000 + */ + u32 clock; + + /* + * tuner + */ +#define AF9033_TUNER_TUA9001 0x27 /* Infineon TUA 9001 */ +#define AF9033_TUNER_FC0011 0x28 /* Fitipower FC0011 */ +#define AF9033_TUNER_MXL5007T 0xa0 /* MaxLinear MxL5007T */ +#define AF9033_TUNER_TDA18218 0xa1 /* NXP TDA 18218HN */ + u8 tuner; + + /* + * TS settings + */ +#define AF9033_TS_MODE_USB 0 +#define AF9033_TS_MODE_PARALLEL 1 +#define AF9033_TS_MODE_SERIAL 2 + u8 ts_mode:2; + + /* + * input spectrum inversion + */ + bool spec_inv; +}; + + +#if defined(CONFIG_DVB_AF9033) || \ + (defined(CONFIG_DVB_AF9033_MODULE) && defined(MODULE)) +extern struct dvb_frontend *af9033_attach(const struct af9033_config *config, + struct i2c_adapter *i2c); +#else +static inline struct dvb_frontend *af9033_attach( + const struct af9033_config *config, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif /* AF9033_H */ diff --git a/drivers/media/dvb/frontends/af9033_priv.h b/drivers/media/dvb/frontends/af9033_priv.h new file mode 100644 index 000000000000..0b783b9ed75e --- /dev/null +++ b/drivers/media/dvb/frontends/af9033_priv.h @@ -0,0 +1,470 @@ +/* + * Afatech AF9033 demodulator driver + * + * Copyright (C) 2009 Antti Palosaari + * Copyright (C) 2012 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef AF9033_PRIV_H +#define AF9033_PRIV_H + +#include "dvb_frontend.h" +#include "af9033.h" + +struct reg_val { + u32 reg; + u8 val; +}; + +struct reg_val_mask { + u32 reg; + u8 val; + u8 mask; +}; + +struct coeff { + u32 clock; + u32 bandwidth_hz; + u8 val[36]; +}; + +struct clock_adc { + u32 clock; + u32 adc; +}; + +struct val_snr { + u32 val; + u8 snr; +}; + +/* Xtal clock vs. ADC clock lookup table */ +static const struct clock_adc clock_adc_lut[] = { + { 16384000, 20480000 }, + { 20480000, 20480000 }, + { 36000000, 20250000 }, + { 30000000, 20156250 }, + { 26000000, 20583333 }, + { 28000000, 20416667 }, + { 32000000, 20500000 }, + { 34000000, 20187500 }, + { 24000000, 20500000 }, + { 22000000, 20625000 }, + { 12000000, 20250000 }, +}; + +/* pre-calculated coeff lookup table */ +static const struct coeff coeff_lut[] = { + /* 12.000 MHz */ + { 12000000, 8000000, { + 0x01, 0xce, 0x55, 0xc9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, + 0x99, 0x0f, 0x00, 0x73, 0x95, 0x72, 0x00, 0x73, 0x91, 0xd5, + 0x00, 0x39, 0xca, 0xb9, 0x00, 0xe7, 0x2a, 0xe4, 0x00, 0x73, + 0x95, 0x72, 0x37, 0x02, 0xce, 0x01 } + }, + { 12000000, 7000000, { + 0x01, 0x94, 0x8b, 0x10, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, + 0x25, 0xed, 0x00, 0x65, 0x22, 0xc4, 0x00, 0x65, 0x1f, 0x9b, + 0x00, 0x32, 0x91, 0x62, 0x00, 0xca, 0x45, 0x88, 0x00, 0x65, + 0x22, 0xc4, 0x88, 0x02, 0x95, 0x01 } + }, + { 12000000, 6000000, { + 0x01, 0x5a, 0xc0, 0x56, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, + 0xb2, 0xcb, 0x00, 0x56, 0xb0, 0x15, 0x00, 0x56, 0xad, 0x60, + 0x00, 0x2b, 0x58, 0x0b, 0x00, 0xad, 0x60, 0x2b, 0x00, 0x56, + 0xb0, 0x15, 0xf4, 0x02, 0x5b, 0x01 } + }, +}; + +/* QPSK SNR lookup table */ +static const struct val_snr qpsk_snr_lut[] = { + { 0x0b4771, 0 }, + { 0x0c1aed, 1 }, + { 0x0d0d27, 2 }, + { 0x0e4d19, 3 }, + { 0x0e5da8, 4 }, + { 0x107097, 5 }, + { 0x116975, 6 }, + { 0x1252d9, 7 }, + { 0x131fa4, 8 }, + { 0x13d5e1, 9 }, + { 0x148e53, 10 }, + { 0x15358b, 11 }, + { 0x15dd29, 12 }, + { 0x168112, 13 }, + { 0x170b61, 14 }, + { 0x17a532, 15 }, + { 0x180f94, 16 }, + { 0x186ed2, 17 }, + { 0x18b271, 18 }, + { 0x18e118, 19 }, + { 0x18ff4b, 20 }, + { 0x190af1, 21 }, + { 0x191451, 22 }, + { 0xffffff, 23 }, +}; + +/* QAM16 SNR lookup table */ +static const struct val_snr qam16_snr_lut[] = { + { 0x04f0d5, 0 }, + { 0x05387a, 1 }, + { 0x0573a4, 2 }, + { 0x05a99e, 3 }, + { 0x05cc80, 4 }, + { 0x05eb62, 5 }, + { 0x05fecf, 6 }, + { 0x060b80, 7 }, + { 0x062501, 8 }, + { 0x064865, 9 }, + { 0x069604, 10 }, + { 0x06f356, 11 }, + { 0x07706a, 12 }, + { 0x0804d3, 13 }, + { 0x089d1a, 14 }, + { 0x093e3d, 15 }, + { 0x09e35d, 16 }, + { 0x0a7c3c, 17 }, + { 0x0afaf8, 18 }, + { 0x0b719d, 19 }, + { 0x0bda6a, 20 }, + { 0x0c0c75, 21 }, + { 0x0c3f7d, 22 }, + { 0x0c5e62, 23 }, + { 0x0c6c31, 24 }, + { 0x0c7925, 25 }, + { 0xffffff, 26 }, +}; + +/* QAM64 SNR lookup table */ +static const struct val_snr qam64_snr_lut[] = { + { 0x0256d0, 0 }, + { 0x027a65, 1 }, + { 0x029873, 2 }, + { 0x02b7fe, 3 }, + { 0x02cf1e, 4 }, + { 0x02e234, 5 }, + { 0x02f409, 6 }, + { 0x030046, 7 }, + { 0x030844, 8 }, + { 0x030a02, 9 }, + { 0x030cde, 10 }, + { 0x031031, 11 }, + { 0x03144c, 12 }, + { 0x0315dd, 13 }, + { 0x031920, 14 }, + { 0x0322d0, 15 }, + { 0x0339fc, 16 }, + { 0x0364a1, 17 }, + { 0x038bcc, 18 }, + { 0x03c7d3, 19 }, + { 0x0408cc, 20 }, + { 0x043bed, 21 }, + { 0x048061, 22 }, + { 0x04be95, 23 }, + { 0x04fa7d, 24 }, + { 0x052405, 25 }, + { 0x05570d, 26 }, + { 0x059feb, 27 }, + { 0x05bf38, 28 }, + { 0xffffff, 29 }, +}; + +static const struct reg_val ofsm_init[] = { + { 0x800051, 0x01 }, + { 0x800070, 0x0a }, + { 0x80007e, 0x04 }, + { 0x800081, 0x0a }, + { 0x80008a, 0x01 }, + { 0x80008e, 0x01 }, + { 0x800092, 0x06 }, + { 0x800099, 0x01 }, + { 0x80009f, 0xe1 }, + { 0x8000a0, 0xcf }, + { 0x8000a3, 0x01 }, + { 0x8000a5, 0x01 }, + { 0x8000a6, 0x01 }, + { 0x8000a9, 0x00 }, + { 0x8000aa, 0x01 }, + { 0x8000ab, 0x01 }, + { 0x8000b0, 0x01 }, + { 0x8000c0, 0x05 }, + { 0x8000c4, 0x19 }, + { 0x80f000, 0x0f }, + { 0x80f016, 0x10 }, + { 0x80f017, 0x04 }, + { 0x80f018, 0x05 }, + { 0x80f019, 0x04 }, + { 0x80f01a, 0x05 }, + { 0x80f021, 0x03 }, + { 0x80f022, 0x0a }, + { 0x80f023, 0x0a }, + { 0x80f02b, 0x00 }, + { 0x80f02c, 0x01 }, + { 0x80f064, 0x03 }, + { 0x80f065, 0xf9 }, + { 0x80f066, 0x03 }, + { 0x80f067, 0x01 }, + { 0x80f06f, 0xe0 }, + { 0x80f070, 0x03 }, + { 0x80f072, 0x0f }, + { 0x80f073, 0x03 }, + { 0x80f078, 0x00 }, + { 0x80f087, 0x00 }, + { 0x80f09b, 0x3f }, + { 0x80f09c, 0x00 }, + { 0x80f09d, 0x20 }, + { 0x80f09e, 0x00 }, + { 0x80f09f, 0x0c }, + { 0x80f0a0, 0x00 }, + { 0x80f130, 0x04 }, + { 0x80f132, 0x04 }, + { 0x80f144, 0x1a }, + { 0x80f146, 0x00 }, + { 0x80f14a, 0x01 }, + { 0x80f14c, 0x00 }, + { 0x80f14d, 0x00 }, + { 0x80f14f, 0x04 }, + { 0x80f158, 0x7f }, + { 0x80f15a, 0x00 }, + { 0x80f15b, 0x08 }, + { 0x80f15d, 0x03 }, + { 0x80f15e, 0x05 }, + { 0x80f163, 0x05 }, + { 0x80f166, 0x01 }, + { 0x80f167, 0x40 }, + { 0x80f168, 0x0f }, + { 0x80f17a, 0x00 }, + { 0x80f17b, 0x00 }, + { 0x80f183, 0x01 }, + { 0x80f19d, 0x40 }, + { 0x80f1bc, 0x36 }, + { 0x80f1bd, 0x00 }, + { 0x80f1cb, 0xa0 }, + { 0x80f1cc, 0x01 }, + { 0x80f204, 0x10 }, + { 0x80f214, 0x00 }, + { 0x80f40e, 0x0a }, + { 0x80f40f, 0x40 }, + { 0x80f410, 0x08 }, + { 0x80f55f, 0x0a }, + { 0x80f561, 0x15 }, + { 0x80f562, 0x20 }, + { 0x80f5df, 0xfb }, + { 0x80f5e0, 0x00 }, + { 0x80f5e3, 0x09 }, + { 0x80f5e4, 0x01 }, + { 0x80f5e5, 0x01 }, + { 0x80f5f8, 0x01 }, + { 0x80f5fd, 0x01 }, + { 0x80f600, 0x05 }, + { 0x80f601, 0x08 }, + { 0x80f602, 0x0b }, + { 0x80f603, 0x0e }, + { 0x80f604, 0x11 }, + { 0x80f605, 0x14 }, + { 0x80f606, 0x17 }, + { 0x80f607, 0x1f }, + { 0x80f60e, 0x00 }, + { 0x80f60f, 0x04 }, + { 0x80f610, 0x32 }, + { 0x80f611, 0x10 }, + { 0x80f707, 0xfc }, + { 0x80f708, 0x00 }, + { 0x80f709, 0x37 }, + { 0x80f70a, 0x00 }, + { 0x80f78b, 0x01 }, + { 0x80f80f, 0x40 }, + { 0x80f810, 0x54 }, + { 0x80f811, 0x5a }, + { 0x80f905, 0x01 }, + { 0x80fb06, 0x03 }, + { 0x80fd8b, 0x00 }, +}; + +/* Infineon TUA 9001 tuner init + AF9033_TUNER_TUA9001 = 0x27 */ +static const struct reg_val tuner_init_tua9001[] = { + { 0x800046, 0x27 }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x80006d, 0x00 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800075, 0x03 }, + { 0x800076, 0x02 }, + { 0x800077, 0x00 }, + { 0x800078, 0x01 }, + { 0x800079, 0x00 }, + { 0x80007a, 0x7e }, + { 0x80007b, 0x3e }, + { 0x800093, 0x00 }, + { 0x800094, 0x01 }, + { 0x800095, 0x02 }, + { 0x800096, 0x01 }, + { 0x800098, 0x0a }, + { 0x80009b, 0x05 }, + { 0x80009c, 0x80 }, + { 0x8000b3, 0x00 }, + { 0x8000c1, 0x01 }, + { 0x8000c2, 0x00 }, + { 0x80f007, 0x00 }, + { 0x80f01f, 0x82 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f047, 0x00 }, + { 0x80f054, 0x00 }, + { 0x80f055, 0x00 }, + { 0x80f077, 0x01 }, + { 0x80f1e6, 0x00 }, +}; + +/* Fitipower fc0011 tuner init + AF9033_TUNER_FC0011 = 0x28 */ +static const struct reg_val tuner_init_fc0011[] = { + { 0x800046, AF9033_TUNER_FC0011 }, + { 0x800057, 0x00 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0xa5 }, + { 0x80006e, 0x01 }, + { 0x800071, 0x0A }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x80009b, 0x2D }, + { 0x80009c, 0x60 }, + { 0x80009d, 0x23 }, + { 0x8000a4, 0x50 }, + { 0x8000ad, 0x50 }, + { 0x8000b3, 0x01 }, + { 0x8000b7, 0x88 }, + { 0x8000b8, 0xa6 }, + { 0x8000c3, 0x01 }, + { 0x8000c4, 0x01 }, + { 0x8000c7, 0x69 }, + { 0x80F007, 0x00 }, + { 0x80F00A, 0x1B }, + { 0x80F00B, 0x1B }, + { 0x80F00C, 0x1B }, + { 0x80F00D, 0x1B }, + { 0x80F00E, 0xFF }, + { 0x80F00F, 0x01 }, + { 0x80F010, 0x00 }, + { 0x80F011, 0x02 }, + { 0x80F012, 0xFF }, + { 0x80F013, 0x01 }, + { 0x80F014, 0x00 }, + { 0x80F015, 0x02 }, + { 0x80F01B, 0xEF }, + { 0x80F01C, 0x01 }, + { 0x80F01D, 0x0f }, + { 0x80F01E, 0x02 }, + { 0x80F01F, 0x6E }, + { 0x80F020, 0x00 }, + { 0x80F025, 0xDE }, + { 0x80F026, 0x00 }, + { 0x80F027, 0x0A }, + { 0x80F028, 0x03 }, + { 0x80F029, 0x6E }, + { 0x80F02A, 0x00 }, + { 0x80F047, 0x00 }, + { 0x80F054, 0x00 }, + { 0x80F055, 0x00 }, + { 0x80F077, 0x01 }, + { 0x80F1E6, 0x00 }, +}; + +/* MaxLinear MxL5007T tuner init + AF9033_TUNER_MXL5007T = 0xa0 */ +static const struct reg_val tuner_init_mxl5007t[] = { + { 0x800046, 0x1b }, + { 0x800057, 0x01 }, + { 0x800058, 0x01 }, + { 0x80005f, 0x00 }, + { 0x800060, 0x00 }, + { 0x800068, 0x96 }, + { 0x800071, 0x05 }, + { 0x800072, 0x02 }, + { 0x800074, 0x01 }, + { 0x800079, 0x01 }, + { 0x800093, 0x00 }, + { 0x800094, 0x00 }, + { 0x800095, 0x00 }, + { 0x800096, 0x00 }, + { 0x8000b3, 0x01 }, + { 0x8000c1, 0x01 }, + { 0x8000c2, 0x00 }, + { 0x80f007, 0x00 }, + { 0x80f00c, 0x19 }, + { 0x80f00d, 0x1a }, + { 0x80f012, 0xda }, + { 0x80f013, 0x00 }, + { 0x80f014, 0x00 }, + { 0x80f015, 0x02 }, + { 0x80f01f, 0x82 }, + { 0x80f020, 0x00 }, + { 0x80f029, 0x82 }, + { 0x80f02a, 0x00 }, + { 0x80f077, 0x02 }, + { 0x80f1e6, 0x00 }, +}; + +/* NXP TDA 18218HN tuner init + AF9033_TUNER_TDA18218 = 0xa1 */ +static const struct reg_val tuner_init_tda18218[] = { + {0x800046, 0xa1}, + {0x800057, 0x01}, + {0x800058, 0x01}, + {0x80005f, 0x00}, + {0x800060, 0x00}, + {0x800071, 0x05}, + {0x800072, 0x02}, + {0x800074, 0x01}, + {0x800079, 0x01}, + {0x800093, 0x00}, + {0x800094, 0x00}, + {0x800095, 0x00}, + {0x800096, 0x00}, + {0x8000b3, 0x01}, + {0x8000c3, 0x01}, + {0x8000c4, 0x00}, + {0x80f007, 0x00}, + {0x80f00c, 0x19}, + {0x80f00d, 0x1a}, + {0x80f012, 0xda}, + {0x80f013, 0x00}, + {0x80f014, 0x00}, + {0x80f015, 0x02}, + {0x80f01f, 0x82}, + {0x80f020, 0x00}, + {0x80f029, 0x82}, + {0x80f02a, 0x00}, + {0x80f077, 0x02}, + {0x80f1e6, 0x00}, +}; + +#endif /* AF9033_PRIV_H */ + diff --git a/drivers/media/dvb/frontends/au8522_common.c b/drivers/media/dvb/frontends/au8522_common.c new file mode 100644 index 000000000000..5cfe151ee394 --- /dev/null +++ b/drivers/media/dvb/frontends/au8522_common.c @@ -0,0 +1,259 @@ +/* + Auvitek AU8522 QAM/8VSB demodulator driver + + Copyright (C) 2008 Steven Toth + Copyright (C) 2008 Devin Heitmueller + Copyright (C) 2005-2008 Auvitek International, Ltd. + Copyright (C) 2012 Michael Krufky + + 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 "au8522_priv.h" + +MODULE_LICENSE("GPL"); + +static int debug; + +#define dprintk(arg...)\ + do { if (debug)\ + printk(arg);\ + } while (0) + +/* Despite the name "hybrid_tuner", the framework works just as well for + hybrid demodulators as well... */ +static LIST_HEAD(hybrid_tuner_instance_list); +static DEFINE_MUTEX(au8522_list_mutex); + +/* 16 bit registers, 8 bit values */ +int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) +{ + int ret; + u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; + + struct i2c_msg msg = { .addr = state->config->demod_address, + .flags = 0, .buf = buf, .len = 3 }; + + ret = i2c_transfer(state->i2c, &msg, 1); + + if (ret != 1) + printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " + "ret == %i)\n", __func__, reg, data, ret); + + return (ret != 1) ? -1 : 0; +} +EXPORT_SYMBOL(au8522_writereg); + +u8 au8522_readreg(struct au8522_state *state, u16 reg) +{ + int ret; + u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff }; + u8 b1[] = { 0 }; + + struct i2c_msg msg[] = { + { .addr = state->config->demod_address, .flags = 0, + .buf = b0, .len = 2 }, + { .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: readreg error (ret == %i)\n", + __func__, ret); + return b1[0]; +} +EXPORT_SYMBOL(au8522_readreg); + +int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct au8522_state *state = fe->demodulator_priv; + + dprintk("%s(%d)\n", __func__, enable); + + if (state->operational_mode == AU8522_ANALOG_MODE) { + /* We're being asked to manage the gate even though we're + not in digital mode. This can occur if we get switched + over to analog mode before the dvb_frontend kernel thread + has completely shutdown */ + return 0; + } + + if (enable) + return au8522_writereg(state, 0x106, 1); + else + return au8522_writereg(state, 0x106, 0); +} +EXPORT_SYMBOL(au8522_i2c_gate_ctrl); + +/* Reset the demod hardware and reset all of the configuration registers + to a default state. */ +int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, + u8 client_address) +{ + int ret; + + mutex_lock(&au8522_list_mutex); + ret = hybrid_tuner_request_state(struct au8522_state, (*state), + hybrid_tuner_instance_list, + i2c, client_address, "au8522"); + mutex_unlock(&au8522_list_mutex); + + return ret; +} +EXPORT_SYMBOL(au8522_get_state); + +void au8522_release_state(struct au8522_state *state) +{ + mutex_lock(&au8522_list_mutex); + if (state != NULL) + hybrid_tuner_release_state(state); + mutex_unlock(&au8522_list_mutex); +} +EXPORT_SYMBOL(au8522_release_state); + +static int au8522_led_gpio_enable(struct au8522_state *state, int onoff) +{ + struct au8522_led_config *led_config = state->config->led_cfg; + u8 val; + + /* bail out if we can't control an LED */ + if (!led_config || !led_config->gpio_output || + !led_config->gpio_output_enable || !led_config->gpio_output_disable) + return 0; + + val = au8522_readreg(state, 0x4000 | + (led_config->gpio_output & ~0xc000)); + if (onoff) { + /* enable GPIO output */ + val &= ~((led_config->gpio_output_enable >> 8) & 0xff); + val |= (led_config->gpio_output_enable & 0xff); + } else { + /* disable GPIO output */ + val &= ~((led_config->gpio_output_disable >> 8) & 0xff); + val |= (led_config->gpio_output_disable & 0xff); + } + return au8522_writereg(state, 0x8000 | + (led_config->gpio_output & ~0xc000), val); +} + +/* led = 0 | off + * led = 1 | signal ok + * led = 2 | signal strong + * led < 0 | only light led if leds are currently off + */ +int au8522_led_ctrl(struct au8522_state *state, int led) +{ + struct au8522_led_config *led_config = state->config->led_cfg; + int i, ret = 0; + + /* bail out if we can't control an LED */ + if (!led_config || !led_config->gpio_leds || + !led_config->num_led_states || !led_config->led_states) + return 0; + + if (led < 0) { + /* if LED is already lit, then leave it as-is */ + if (state->led_state) + return 0; + else + led *= -1; + } + + /* toggle LED if changing state */ + if (state->led_state != led) { + u8 val; + + dprintk("%s: %d\n", __func__, led); + + au8522_led_gpio_enable(state, 1); + + val = au8522_readreg(state, 0x4000 | + (led_config->gpio_leds & ~0xc000)); + + /* start with all leds off */ + for (i = 0; i < led_config->num_led_states; i++) + val &= ~led_config->led_states[i]; + + /* set selected LED state */ + if (led < led_config->num_led_states) + val |= led_config->led_states[led]; + else if (led_config->num_led_states) + val |= + led_config->led_states[led_config->num_led_states - 1]; + + ret = au8522_writereg(state, 0x8000 | + (led_config->gpio_leds & ~0xc000), val); + if (ret < 0) + return ret; + + state->led_state = led; + + if (led == 0) + au8522_led_gpio_enable(state, 0); + } + + return 0; +} +EXPORT_SYMBOL(au8522_led_ctrl); + +int au8522_init(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + state->operational_mode = AU8522_DIGITAL_MODE; + + /* Clear out any state associated with the digital side of the + chip, so that when it gets powered back up it won't think + that it is already tuned */ + state->current_frequency = 0; + + au8522_writereg(state, 0xa4, 1 << 5); + + au8522_i2c_gate_ctrl(fe, 1); + + return 0; +} +EXPORT_SYMBOL(au8522_init); + +int au8522_sleep(struct dvb_frontend *fe) +{ + struct au8522_state *state = fe->demodulator_priv; + dprintk("%s()\n", __func__); + + /* Only power down if the digital side is currently using the chip */ + if (state->operational_mode == AU8522_ANALOG_MODE) { + /* We're not in one of the expected power modes, which means + that the DVB thread is probably telling us to go to sleep + even though the analog frontend has already started using + the chip. So ignore the request */ + return 0; + } + + /* turn off led */ + au8522_led_ctrl(state, 0); + + /* Power down the chip */ + au8522_writereg(state, 0xa4, 1 << 5); + + state->current_frequency = 0; + + return 0; +} +EXPORT_SYMBOL(au8522_sleep); diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c index 25f650934c73..5fc70d6cd04f 100644 --- a/drivers/media/dvb/frontends/au8522_dig.c +++ b/drivers/media/dvb/frontends/au8522_dig.c @@ -30,74 +30,11 @@ static int debug; -/* Despite the name "hybrid_tuner", the framework works just as well for - hybrid demodulators as well... */ -static LIST_HEAD(hybrid_tuner_instance_list); -static DEFINE_MUTEX(au8522_list_mutex); - #define dprintk(arg...)\ do { if (debug)\ printk(arg);\ } while (0) -/* 16 bit registers, 8 bit values */ -int au8522_writereg(struct au8522_state *state, u16 reg, u8 data) -{ - int ret; - u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data }; - - struct i2c_msg msg = { .addr = state->config->demod_address, - .flags = 0, .buf = buf, .len = 3 }; - - ret = i2c_transfer(state->i2c, &msg, 1); - - if (ret != 1) - printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, " - "ret == %i)\n", __func__, reg, data, ret); - - return (ret != 1) ? -1 : 0; -} - -u8 au8522_readreg(struct au8522_state *state, u16 reg) -{ - int ret; - u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff }; - u8 b1[] = { 0 }; - - struct i2c_msg msg[] = { - { .addr = state->config->demod_address, .flags = 0, - .buf = b0, .len = 2 }, - { .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: readreg error (ret == %i)\n", - __func__, ret); - return b1[0]; -} - -static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) -{ - struct au8522_state *state = fe->demodulator_priv; - - dprintk("%s(%d)\n", __func__, enable); - - if (state->operational_mode == AU8522_ANALOG_MODE) { - /* We're being asked to manage the gate even though we're - not in digital mode. This can occur if we get switched - over to analog mode before the dvb_frontend kernel thread - has completely shutdown */ - return 0; - } - - if (enable) - return au8522_writereg(state, 0x106, 1); - else - return au8522_writereg(state, 0x106, 0); -} - struct mse2snr_tab { u16 val; u16 data; @@ -609,136 +546,6 @@ static int au8522_set_frontend(struct dvb_frontend *fe) return 0; } -/* Reset the demod hardware and reset all of the configuration registers - to a default state. */ -int au8522_init(struct dvb_frontend *fe) -{ - struct au8522_state *state = fe->demodulator_priv; - dprintk("%s()\n", __func__); - - state->operational_mode = AU8522_DIGITAL_MODE; - - /* Clear out any state associated with the digital side of the - chip, so that when it gets powered back up it won't think - that it is already tuned */ - state->current_frequency = 0; - - au8522_writereg(state, 0xa4, 1 << 5); - - au8522_i2c_gate_ctrl(fe, 1); - - return 0; -} - -static int au8522_led_gpio_enable(struct au8522_state *state, int onoff) -{ - struct au8522_led_config *led_config = state->config->led_cfg; - u8 val; - - /* bail out if we can't control an LED */ - if (!led_config || !led_config->gpio_output || - !led_config->gpio_output_enable || !led_config->gpio_output_disable) - return 0; - - val = au8522_readreg(state, 0x4000 | - (led_config->gpio_output & ~0xc000)); - if (onoff) { - /* enable GPIO output */ - val &= ~((led_config->gpio_output_enable >> 8) & 0xff); - val |= (led_config->gpio_output_enable & 0xff); - } else { - /* disable GPIO output */ - val &= ~((led_config->gpio_output_disable >> 8) & 0xff); - val |= (led_config->gpio_output_disable & 0xff); - } - return au8522_writereg(state, 0x8000 | - (led_config->gpio_output & ~0xc000), val); -} - -/* led = 0 | off - * led = 1 | signal ok - * led = 2 | signal strong - * led < 0 | only light led if leds are currently off - */ -static int au8522_led_ctrl(struct au8522_state *state, int led) -{ - struct au8522_led_config *led_config = state->config->led_cfg; - int i, ret = 0; - - /* bail out if we can't control an LED */ - if (!led_config || !led_config->gpio_leds || - !led_config->num_led_states || !led_config->led_states) - return 0; - - if (led < 0) { - /* if LED is already lit, then leave it as-is */ - if (state->led_state) - return 0; - else - led *= -1; - } - - /* toggle LED if changing state */ - if (state->led_state != led) { - u8 val; - - dprintk("%s: %d\n", __func__, led); - - au8522_led_gpio_enable(state, 1); - - val = au8522_readreg(state, 0x4000 | - (led_config->gpio_leds & ~0xc000)); - - /* start with all leds off */ - for (i = 0; i < led_config->num_led_states; i++) - val &= ~led_config->led_states[i]; - - /* set selected LED state */ - if (led < led_config->num_led_states) - val |= led_config->led_states[led]; - else if (led_config->num_led_states) - val |= - led_config->led_states[led_config->num_led_states - 1]; - - ret = au8522_writereg(state, 0x8000 | - (led_config->gpio_leds & ~0xc000), val); - if (ret < 0) - return ret; - - state->led_state = led; - - if (led == 0) - au8522_led_gpio_enable(state, 0); - } - - return 0; -} - -int au8522_sleep(struct dvb_frontend *fe) -{ - struct au8522_state *state = fe->demodulator_priv; - dprintk("%s()\n", __func__); - - /* Only power down if the digital side is currently using the chip */ - if (state->operational_mode == AU8522_ANALOG_MODE) { - /* We're not in one of the expected power modes, which means - that the DVB thread is probably telling us to go to sleep - even though the analog frontend has already started using - the chip. So ignore the request */ - return 0; - } - - /* turn off led */ - au8522_led_ctrl(state, 0); - - /* Power down the chip */ - au8522_writereg(state, 0xa4, 1 << 5); - - state->current_frequency = 0; - - return 0; -} - static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct au8522_state *state = fe->demodulator_priv; @@ -931,28 +738,6 @@ static int au8522_get_tune_settings(struct dvb_frontend *fe, static struct dvb_frontend_ops au8522_ops; -int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, - u8 client_address) -{ - int ret; - - mutex_lock(&au8522_list_mutex); - ret = hybrid_tuner_request_state(struct au8522_state, (*state), - hybrid_tuner_instance_list, - i2c, client_address, "au8522"); - mutex_unlock(&au8522_list_mutex); - - return ret; -} - -void au8522_release_state(struct au8522_state *state) -{ - mutex_lock(&au8522_list_mutex); - if (state != NULL) - hybrid_tuner_release_state(state); - mutex_unlock(&au8522_list_mutex); -} - static void au8522_release(struct dvb_frontend *fe) { diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h index 751e17d692a9..6e4a438732b5 100644 --- a/drivers/media/dvb/frontends/au8522_priv.h +++ b/drivers/media/dvb/frontends/au8522_priv.h @@ -81,6 +81,8 @@ int au8522_sleep(struct dvb_frontend *fe); int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c, u8 client_address); void au8522_release_state(struct au8522_state *state); +int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable); +int au8522_led_ctrl(struct au8522_state *state, int led); /* REGISTERS */ #define AU8522_INPUT_CONTROL_REG081H 0x081 diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c index 5101f10f2d7a..98ecaf0900d6 100644 --- a/drivers/media/dvb/frontends/cx24110.c +++ b/drivers/media/dvb/frontends/cx24110.c @@ -512,14 +512,13 @@ static int cx24110_read_snr(struct dvb_frontend* fe, u16* snr) static int cx24110_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks) { struct cx24110_state *state = fe->demodulator_priv; - u32 lastbyer; if(cx24110_readreg(state,0x10)&0x40) { /* the RS error counter has finished one counting window */ cx24110_writereg(state,0x10,0x60); /* select the byer reg */ - lastbyer=cx24110_readreg(state,0x12)| - (cx24110_readreg(state,0x13)<<8)| - (cx24110_readreg(state,0x14)<<16); + cx24110_readreg(state, 0x12) | + (cx24110_readreg(state, 0x13) << 8) | + (cx24110_readreg(state, 0x14) << 16); cx24110_writereg(state,0x10,0x70); /* select the bler reg */ state->lastbler=cx24110_readreg(state,0x12)| (cx24110_readreg(state,0x13)<<8)| diff --git a/drivers/media/dvb/frontends/cxd2820r_core.c b/drivers/media/dvb/frontends/cxd2820r_core.c index 5c7c2aaf9bf5..3bba37d74f57 100644 --- a/drivers/media/dvb/frontends/cxd2820r_core.c +++ b/drivers/media/dvb/frontends/cxd2820r_core.c @@ -526,12 +526,12 @@ static enum dvbfe_search cxd2820r_search(struct dvb_frontend *fe) if (ret) goto error; - if (status & FE_HAS_SIGNAL) + if (status & FE_HAS_LOCK) break; } /* check if we have a valid signal */ - if (status) { + if (status & FE_HAS_LOCK) { priv->last_tune_failed = 0; return DVBFE_ALGO_SEARCH_SUCCESS; } else { diff --git a/drivers/media/dvb/frontends/dib7000p.c b/drivers/media/dvb/frontends/dib7000p.c index 5ceadc285b3a..3e1eefada0e8 100644 --- a/drivers/media/dvb/frontends/dib7000p.c +++ b/drivers/media/dvb/frontends/dib7000p.c @@ -2396,11 +2396,6 @@ struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, more common) */ st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; - /* FIXME: make sure the dev.parent field is initialized, or else - request_firmware() will hit an OOPS (this should be moved somewhere - more common) */ - st->i2c_master.gated_tuner_i2c_adap.dev.parent = i2c_adap->dev.parent; - dibx000_init_i2c_master(&st->i2c_master, DIB7000P, st->i2c_adap, st->i2c_addr); /* init 7090 tuner adapter */ diff --git a/drivers/media/dvb/frontends/dib9000.c b/drivers/media/dvb/frontends/dib9000.c index 80848b4c15d4..6201c59a78dd 100644 --- a/drivers/media/dvb/frontends/dib9000.c +++ b/drivers/media/dvb/frontends/dib9000.c @@ -31,13 +31,6 @@ struct i2c_device { u8 *i2c_write_buffer; }; -/* lock */ -#define DIB_LOCK struct mutex -#define DibAcquireLock(lock) mutex_lock_interruptible(lock) -#define DibReleaseLock(lock) mutex_unlock(lock) -#define DibInitLock(lock) mutex_init(lock) -#define DibFreeLock(lock) - struct dib9000_pid_ctrl { #define DIB9000_PID_FILTER_CTRL 0 #define DIB9000_PID_FILTER 1 @@ -82,11 +75,11 @@ struct dib9000_state { } fe_mm[18]; u8 memcmd; - DIB_LOCK mbx_if_lock; /* to protect read/write operations */ - DIB_LOCK mbx_lock; /* to protect the whole mailbox handling */ + struct mutex mbx_if_lock; /* to protect read/write operations */ + struct mutex mbx_lock; /* to protect the whole mailbox handling */ - DIB_LOCK mem_lock; /* to protect the memory accesses */ - DIB_LOCK mem_mbx_lock; /* to protect the memory-based mailbox */ + struct mutex mem_lock; /* to protect the memory accesses */ + struct mutex mem_mbx_lock; /* to protect the memory-based mailbox */ #define MBX_MAX_WORDS (256 - 200 - 2) #define DIB9000_MSG_CACHE_SIZE 2 @@ -108,7 +101,7 @@ struct dib9000_state { struct i2c_msg msg[2]; u8 i2c_write_buffer[255]; u8 i2c_read_buffer[255]; - DIB_LOCK demod_lock; + struct mutex demod_lock; u8 get_frontend_internal; struct dib9000_pid_ctrl pid_ctrl[10]; s8 pid_ctrl_index; /* -1: empty list; -2: do not use the list */ @@ -446,13 +439,13 @@ static int dib9000_risc_mem_read(struct dib9000_state *state, u8 cmd, u8 * b, u1 if (!state->platform.risc.fw_is_running) return -EIO; - if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } dib9000_risc_mem_setup(state, cmd | 0x80); dib9000_risc_mem_read_chunks(state, b, len); - DibReleaseLock(&state->platform.risc.mem_lock); + mutex_unlock(&state->platform.risc.mem_lock); return 0; } @@ -462,13 +455,13 @@ static int dib9000_risc_mem_write(struct dib9000_state *state, u8 cmd, const u8 if (!state->platform.risc.fw_is_running) return -EIO; - if (DibAcquireLock(&state->platform.risc.mem_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } dib9000_risc_mem_setup(state, cmd); dib9000_risc_mem_write_chunks(state, b, m->size); - DibReleaseLock(&state->platform.risc.mem_lock); + mutex_unlock(&state->platform.risc.mem_lock); return 0; } @@ -537,7 +530,7 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, if (!state->platform.risc.fw_is_running) return -EINVAL; - if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -584,7 +577,7 @@ static int dib9000_mbx_send_attr(struct dib9000_state *state, u8 id, u16 * data, ret = (u8) dib9000_write_word_attr(state, 1043, 1 << 14, attr); out: - DibReleaseLock(&state->platform.risc.mbx_if_lock); + mutex_unlock(&state->platform.risc.mbx_if_lock); return ret; } @@ -602,7 +595,7 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, if (!state->platform.risc.fw_is_running) return 0; - if (DibAcquireLock(&state->platform.risc.mbx_if_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mbx_if_lock) < 0) { dprintk("could not get the lock"); return 0; } @@ -643,7 +636,7 @@ static u8 dib9000_mbx_read(struct dib9000_state *state, u16 * data, u8 risc_id, /* Update register nb_mes_in_TX */ dib9000_write_word_attr(state, 1028 + mc_base, 1 << 14, attr); - DibReleaseLock(&state->platform.risc.mbx_if_lock); + mutex_unlock(&state->platform.risc.mbx_if_lock); return size + 1; } @@ -708,12 +701,11 @@ static u8 dib9000_mbx_count(struct dib9000_state *state, u8 risc_id, u16 attr) static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) { int ret = 0; - u16 tmp; if (!state->platform.risc.fw_is_running) return -1; - if (DibAcquireLock(&state->platform.risc.mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mbx_lock) < 0) { dprintk("could not get the lock"); return -1; } @@ -721,10 +713,10 @@ static int dib9000_mbx_process(struct dib9000_state *state, u16 attr) if (dib9000_mbx_count(state, 1, attr)) /* 1=RiscB */ ret = dib9000_mbx_fetch_to_cache(state, attr); - tmp = dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ + dib9000_read_word_attr(state, 1229, attr); /* Clear the IRQ */ /* if (tmp) */ /* dprintk( "cleared IRQ: %x", tmp); */ - DibReleaseLock(&state->platform.risc.mbx_lock); + mutex_unlock(&state->platform.risc.mbx_lock); return ret; } @@ -1193,7 +1185,7 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe) struct dibDVBTChannel *ch; int ret = 0; - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -1323,7 +1315,7 @@ static int dib9000_fw_get_channel(struct dvb_frontend *fe) } error: - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); return ret; } @@ -1678,7 +1670,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2 p[12] = 0; } - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); return 0; } @@ -1692,7 +1684,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2 /* do the transaction */ if (dib9000_fw_memmbx_sync(state, FE_SYNC_COMPONENT_ACCESS) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); return 0; } @@ -1700,7 +1692,7 @@ static int dib9000_fw_component_bus_xfer(struct i2c_adapter *i2c_adap, struct i2 if ((num > 1) && (msg[1].flags & I2C_M_RD)) dib9000_risc_mem_read(state, FE_MM_RW_COMPONENT_ACCESS_BUFFER, msg[1].buf, msg[1].len); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); return num; } @@ -1789,7 +1781,7 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) return 0; } - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -1799,7 +1791,7 @@ int dib9000_fw_pid_filter_ctrl(struct dvb_frontend *fe, u8 onoff) dprintk("PID filter enabled %d", onoff); ret = dib9000_write_word(state, 294 + 1, val); - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -1824,14 +1816,14 @@ int dib9000_fw_pid_filter(struct dvb_frontend *fe, u8 id, u16 pid, u8 onoff) return 0; } - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } dprintk("Index %x, PID %d, OnOff %d", id, pid, onoff); ret = dib9000_write_word(state, 300 + 1 + id, onoff ? (1 << 13) | pid : 0); - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } EXPORT_SYMBOL(dib9000_fw_pid_filter); @@ -1851,11 +1843,6 @@ static void dib9000_release(struct dvb_frontend *demod) for (index_frontend = 1; (index_frontend < MAX_NUMBER_OF_FRONTENDS) && (st->fe[index_frontend] != NULL); index_frontend++) dvb_frontend_detach(st->fe[index_frontend]); - DibFreeLock(&state->platform.risc.mbx_if_lock); - DibFreeLock(&state->platform.risc.mbx_lock); - DibFreeLock(&state->platform.risc.mem_lock); - DibFreeLock(&state->platform.risc.mem_mbx_lock); - DibFreeLock(&state->demod_lock); dibx000_exit_i2c_master(&st->i2c_master); i2c_del_adapter(&st->tuner_adap); @@ -1875,7 +1862,7 @@ static int dib9000_sleep(struct dvb_frontend *fe) u8 index_frontend; int ret = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -1887,7 +1874,7 @@ static int dib9000_sleep(struct dvb_frontend *fe) ret = dib9000_mbx_send(state, OUT_MSG_FE_SLEEP, NULL, 0); error: - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -1905,7 +1892,7 @@ static int dib9000_get_frontend(struct dvb_frontend *fe) int ret = 0; if (state->get_frontend_internal == 0) { - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -1964,7 +1951,7 @@ static int dib9000_get_frontend(struct dvb_frontend *fe) return_value: if (state->get_frontend_internal == 0) - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -2012,7 +1999,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe) } state->pid_ctrl_index = -1; /* postpone the pid filtering cmd */ - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return 0; } @@ -2081,7 +2068,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe) /* check the tune result */ if (exit_condition == 1) { /* tune failed */ dprintk("tune failed"); - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); /* tune failed; put all the pid filtering cmd to junk */ state->pid_ctrl_index = -1; return 0; @@ -2137,7 +2124,7 @@ static int dib9000_set_frontend(struct dvb_frontend *fe) /* turn off the diversity for the last frontend */ dib9000_fw_set_diversity_in(state->fe[index_frontend - 1], 0); - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); if (state->pid_ctrl_index >= 0) { u8 index_pid_filter_cmd; u8 pid_ctrl_index = state->pid_ctrl_index; @@ -2175,7 +2162,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) u8 index_frontend; u16 lock = 0, lock_slave = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -2197,7 +2184,7 @@ static int dib9000_read_status(struct dvb_frontend *fe, fe_status_t * stat) if ((lock & 0x0008) || (lock_slave & 0x0008)) *stat |= FE_HAS_LOCK; - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return 0; } @@ -2208,30 +2195,30 @@ static int dib9000_read_ber(struct dvb_frontend *fe, u32 * ber) u16 *c; int ret = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); ret = -EINTR; goto error; } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); ret = -EIO; goto error; } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, state->i2c_read_buffer, 16 * 2); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); c = (u16 *)state->i2c_read_buffer; *ber = c[10] << 16 | c[11]; error: - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -2243,7 +2230,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) u16 val; int ret = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -2256,18 +2243,18 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) *strength += val; } - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); ret = -EINTR; goto error; } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); ret = -EIO; goto error; } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); val = 65535 - c[4]; if (val > 65535 - *strength) @@ -2276,7 +2263,7 @@ static int dib9000_read_signal_strength(struct dvb_frontend *fe, u16 * strength) *strength += val; error: - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -2287,16 +2274,16 @@ static u32 dib9000_get_snr(struct dvb_frontend *fe) u32 n, s, exp; u16 val; - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); return 0; } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); return 0; } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); val = c[7]; n = (val >> 4) & 0xff; @@ -2326,7 +2313,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) u8 index_frontend; u32 snr_master; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } @@ -2340,7 +2327,7 @@ static int dib9000_read_snr(struct dvb_frontend *fe, u16 * snr) } else *snr = 0; - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return 0; } @@ -2351,27 +2338,27 @@ static int dib9000_read_unc_blocks(struct dvb_frontend *fe, u32 * unc) u16 *c = (u16 *)state->i2c_read_buffer; int ret = 0; - if (DibAcquireLock(&state->demod_lock) < 0) { + if (mutex_lock_interruptible(&state->demod_lock) < 0) { dprintk("could not get the lock"); return -EINTR; } - if (DibAcquireLock(&state->platform.risc.mem_mbx_lock) < 0) { + if (mutex_lock_interruptible(&state->platform.risc.mem_mbx_lock) < 0) { dprintk("could not get the lock"); ret = -EINTR; goto error; } if (dib9000_fw_memmbx_sync(state, FE_SYNC_CHANNEL) < 0) { - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); ret = -EIO; goto error; } dib9000_risc_mem_read(state, FE_MM_R_FE_MONITOR, (u8 *) c, 16 * 2); - DibReleaseLock(&state->platform.risc.mem_mbx_lock); + mutex_unlock(&state->platform.risc.mem_mbx_lock); *unc = c[12]; error: - DibReleaseLock(&state->demod_lock); + mutex_unlock(&state->demod_lock); return ret; } @@ -2514,11 +2501,11 @@ struct dvb_frontend *dib9000_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, c st->gpio_val = DIB9000_GPIO_DEFAULT_VALUES; st->gpio_pwm_pos = DIB9000_GPIO_DEFAULT_PWM_POS; - DibInitLock(&st->platform.risc.mbx_if_lock); - DibInitLock(&st->platform.risc.mbx_lock); - DibInitLock(&st->platform.risc.mem_lock); - DibInitLock(&st->platform.risc.mem_mbx_lock); - DibInitLock(&st->demod_lock); + mutex_init(&st->platform.risc.mbx_if_lock); + mutex_init(&st->platform.risc.mbx_lock); + mutex_init(&st->platform.risc.mem_lock); + mutex_init(&st->platform.risc.mem_mbx_lock); + mutex_init(&st->demod_lock); st->get_frontend_internal = 0; st->pid_ctrl_index = -2; diff --git a/drivers/media/dvb/frontends/drxd.h b/drivers/media/dvb/frontends/drxd.h index 34398738f9bc..216c8c3702f8 100644 --- a/drivers/media/dvb/frontends/drxd.h +++ b/drivers/media/dvb/frontends/drxd.h @@ -51,9 +51,23 @@ struct drxd_config { s16(*osc_deviation) (void *priv, s16 dev, int flag); }; +#if defined(CONFIG_DVB_DRXD) || \ + (defined(CONFIG_DVB_DRXD_MODULE) && defined(MODULE)) extern struct dvb_frontend *drxd_attach(const struct drxd_config *config, void *priv, struct i2c_adapter *i2c, struct device *dev); +#else +static inline +struct dvb_frontend *drxd_attach(const struct drxd_config *config, + void *priv, struct i2c_adapter *i2c, + struct device *dev) +{ + printk(KERN_INFO "%s: not probed - driver disabled by Kconfig\n", + __func__); + return NULL; +} +#endif + extern int drxd_config_i2c(struct dvb_frontend *, int); #endif diff --git a/drivers/media/dvb/frontends/drxk_hard.c b/drivers/media/dvb/frontends/drxk_hard.c index a414b1f2b6a5..60b868faeacf 100644 --- a/drivers/media/dvb/frontends/drxk_hard.c +++ b/drivers/media/dvb/frontends/drxk_hard.c @@ -1380,20 +1380,20 @@ static int DownloadMicrocode(struct drxk_state *state, const u8 pMCImage[], u32 Length) { const u8 *pSrc = pMCImage; - u16 Flags; - u16 Drain; u32 Address; u16 nBlocks; u16 BlockSize; - u16 BlockCRC; u32 offset = 0; u32 i; int status = 0; dprintk(1, "\n"); - /* down the drain (we don care about MAGIC_WORD) */ + /* down the drain (we don't care about MAGIC_WORD) */ +#if 0 + /* For future reference */ Drain = (pSrc[0] << 8) | pSrc[1]; +#endif pSrc += sizeof(u16); offset += sizeof(u16); nBlocks = (pSrc[0] << 8) | pSrc[1]; @@ -1410,11 +1410,17 @@ static int DownloadMicrocode(struct drxk_state *state, pSrc += sizeof(u16); offset += sizeof(u16); +#if 0 + /* For future reference */ Flags = (pSrc[0] << 8) | pSrc[1]; +#endif pSrc += sizeof(u16); offset += sizeof(u16); +#if 0 + /* For future reference */ BlockCRC = (pSrc[0] << 8) | pSrc[1]; +#endif pSrc += sizeof(u16); offset += sizeof(u16); @@ -5829,7 +5835,7 @@ static int WriteGPIO(struct drxk_state *state) } if (state->UIO_mask & 0x0002) { /* UIO-2 */ /* write to io pad configuration register - output mode */ - status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg); + status = write16(state, SIO_PDR_SMA_RX_CFG__A, state->m_GPIOCfg); if (status < 0) goto error; @@ -5848,7 +5854,7 @@ static int WriteGPIO(struct drxk_state *state) } if (state->UIO_mask & 0x0004) { /* UIO-3 */ /* write to io pad configuration register - output mode */ - status = write16(state, SIO_PDR_SMA_TX_CFG__A, state->m_GPIOCfg); + status = write16(state, SIO_PDR_GPIO_CFG__A, state->m_GPIOCfg); if (status < 0) goto error; diff --git a/drivers/media/dvb/frontends/drxk_map.h b/drivers/media/dvb/frontends/drxk_map.h index 9b11a8328869..23e16c12f234 100644 --- a/drivers/media/dvb/frontends/drxk_map.h +++ b/drivers/media/dvb/frontends/drxk_map.h @@ -432,6 +432,7 @@ #define SIO_PDR_UIO_OUT_LO__A 0x7F0016 #define SIO_PDR_OHW_CFG__A 0x7F001F #define SIO_PDR_OHW_CFG_FREF_SEL__M 0x3 +#define SIO_PDR_GPIO_CFG__A 0x7F0021 #define SIO_PDR_MSTRT_CFG__A 0x7F0025 #define SIO_PDR_MERR_CFG__A 0x7F0026 #define SIO_PDR_MCLK_CFG__A 0x7F0028 @@ -446,4 +447,5 @@ #define SIO_PDR_MD5_CFG__A 0x7F0030 #define SIO_PDR_MD6_CFG__A 0x7F0031 #define SIO_PDR_MD7_CFG__A 0x7F0032 +#define SIO_PDR_SMA_RX_CFG__A 0x7F0037 #define SIO_PDR_SMA_TX_CFG__A 0x7F0038 diff --git a/drivers/media/dvb/frontends/ds3000.c b/drivers/media/dvb/frontends/ds3000.c index af65d013db11..4c8ac2657c4a 100644 --- a/drivers/media/dvb/frontends/ds3000.c +++ b/drivers/media/dvb/frontends/ds3000.c @@ -1114,7 +1114,10 @@ static int ds3000_set_frontend(struct dvb_frontend *fe) ds3000_writereg(state, ds3000_dvbs2_init_tab[i], ds3000_dvbs2_init_tab[i + 1]); - ds3000_writereg(state, 0xfe, 0x98); + if (c->symbol_rate >= 30000000) + ds3000_writereg(state, 0xfe, 0x54); + else + ds3000_writereg(state, 0xfe, 0x98); break; default: return 1; diff --git a/drivers/media/dvb/frontends/it913x-fe.c b/drivers/media/dvb/frontends/it913x-fe.c index 84df03c29179..708cbf197913 100644 --- a/drivers/media/dvb/frontends/it913x-fe.c +++ b/drivers/media/dvb/frontends/it913x-fe.c @@ -633,10 +633,9 @@ static int it913x_fe_read_snr(struct dvb_frontend *fe, u16 *snr) static int it913x_fe_read_ber(struct dvb_frontend *fe, u32 *ber) { struct it913x_fe_state *state = fe->demodulator_priv; - int ret; u8 reg[5]; /* Read Aborted Packets and Pre-Viterbi error rate 5 bytes */ - ret = it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg)); + it913x_read_reg(state, RSD_ABORT_PKT_LSB, reg, sizeof(reg)); state->ucblocks += (u32)(reg[1] << 8) | reg[0]; *ber = (u32)(reg[4] << 16) | (reg[3] << 8) | reg[2]; return 0; @@ -658,10 +657,9 @@ static int it913x_fe_get_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct it913x_fe_state *state = fe->demodulator_priv; - int ret; u8 reg[8]; - ret = it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg)); + it913x_read_reg(state, REG_TPSD_TX_MODE, reg, sizeof(reg)); if (reg[3] < 3) p->modulation = fe_con[reg[3]]; @@ -691,25 +689,25 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe) { struct dtv_frontend_properties *p = &fe->dtv_property_cache; struct it913x_fe_state *state = fe->demodulator_priv; - int ret, i; + int i; u8 empty_ch, last_ch; state->it913x_status = 0; /* Set bw*/ - ret = it913x_fe_select_bw(state, p->bandwidth_hz, + it913x_fe_select_bw(state, p->bandwidth_hz, state->adcFrequency); /* Training Mode Off */ - ret = it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0); + it913x_write_reg(state, PRO_LINK, TRAINING_MODE, 0x0); /* Clear Empty Channel */ - ret = it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0); + it913x_write_reg(state, PRO_DMOD, EMPTY_CHANNEL_STATUS, 0x0); /* Clear bits */ - ret = it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0); + it913x_write_reg(state, PRO_DMOD, MP2IF_SYNC_LK, 0x0); /* LED on */ - ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); + it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x1); /* Select Band*/ if ((p->frequency >= 51000000) && (p->frequency <= 230000000)) i = 0; @@ -720,7 +718,7 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe) else return -EOPNOTSUPP; - ret = it913x_write_reg(state, PRO_DMOD, FREE_BAND, i); + it913x_write_reg(state, PRO_DMOD, FREE_BAND, i); deb_info("Frontend Set Tuner Type %02x", state->tuner_type); switch (state->tuner_type) { @@ -730,7 +728,7 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe) case IT9135_60: case IT9135_61: case IT9135_62: - ret = it9137_set_tuner(state, + it9137_set_tuner(state, p->bandwidth_hz, p->frequency); break; default: @@ -742,9 +740,9 @@ static int it913x_fe_set_frontend(struct dvb_frontend *fe) break; } /* LED off */ - ret = it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); + it913x_write_reg(state, PRO_LINK, GPIOH3_O, 0x0); /* Trigger ofsm */ - ret = it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); + it913x_write_reg(state, PRO_DMOD, TRIGGER_OFSM, 0x0); last_ch = 2; for (i = 0; i < 40; ++i) { empty_ch = it913x_read_reg_u8(state, EMPTY_CHANNEL_STATUS); diff --git a/drivers/media/dvb/frontends/lg2160.c b/drivers/media/dvb/frontends/lg2160.c new file mode 100644 index 000000000000..a3ab1a5b6597 --- /dev/null +++ b/drivers/media/dvb/frontends/lg2160.c @@ -0,0 +1,1468 @@ +/* + * Support for LG2160 - ATSC/MH + * + * Copyright (C) 2010 Michael Krufky + * + * 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 "lg2160.h" + +static int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))"); + +#define DBG_INFO 1 +#define DBG_REG 2 + +#define lg_printk(kern, fmt, arg...) \ + printk(kern "%s: " fmt, __func__, ##arg) + +#define lg_info(fmt, arg...) printk(KERN_INFO "lg2160: " fmt, ##arg) +#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg) +#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg) +#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \ + lg_printk(KERN_DEBUG, fmt, ##arg) +#define lg_reg(fmt, arg...) if (debug & DBG_REG) \ + lg_printk(KERN_DEBUG, fmt, ##arg) + +#define lg_fail(ret) \ +({ \ + int __ret; \ + __ret = (ret < 0); \ + if (__ret) \ + lg_err("error %d on line %d\n", ret, __LINE__); \ + __ret; \ +}) + +struct lg216x_state { + struct i2c_adapter *i2c_adap; + const struct lg2160_config *cfg; + + struct dvb_frontend frontend; + + u32 current_frequency; + u8 parade_id; + u8 fic_ver; + unsigned int last_reset; +}; + +/* ------------------------------------------------------------------------ */ + +static int lg216x_write_reg(struct lg216x_state *state, u16 reg, u8 val) +{ + int ret; + u8 buf[] = { reg >> 8, reg & 0xff, val }; + struct i2c_msg msg = { + .addr = state->cfg->i2c_addr, .flags = 0, + .buf = buf, .len = 3, + }; + + lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val); + + ret = i2c_transfer(state->i2c_adap, &msg, 1); + + if (ret != 1) { + lg_err("error (addr %02x %02x <- %02x, err = %i)\n", + msg.buf[0], msg.buf[1], msg.buf[2], ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +static int lg216x_read_reg(struct lg216x_state *state, u16 reg, u8 *val) +{ + int ret; + u8 reg_buf[] = { reg >> 8, reg & 0xff }; + struct i2c_msg msg[] = { + { .addr = state->cfg->i2c_addr, + .flags = 0, .buf = reg_buf, .len = 2 }, + { .addr = state->cfg->i2c_addr, + .flags = I2C_M_RD, .buf = val, .len = 1 }, + }; + + lg_reg("reg: 0x%04x\n", reg); + + ret = i2c_transfer(state->i2c_adap, msg, 2); + + if (ret != 2) { + lg_err("error (addr %02x reg %04x error (ret == %i)\n", + state->cfg->i2c_addr, reg, ret); + if (ret < 0) + return ret; + else + return -EREMOTEIO; + } + return 0; +} + +struct lg216x_reg { + u16 reg; + u8 val; +}; + +static int lg216x_write_regs(struct lg216x_state *state, + struct lg216x_reg *regs, int len) +{ + int i, ret; + + lg_reg("writing %d registers...\n", len); + + for (i = 0; i < len - 1; i++) { + ret = lg216x_write_reg(state, regs[i].reg, regs[i].val); + if (lg_fail(ret)) + return ret; + } + return 0; +} + +static int lg216x_set_reg_bit(struct lg216x_state *state, + u16 reg, int bit, int onoff) +{ + u8 val; + int ret; + + lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff); + + ret = lg216x_read_reg(state, reg, &val); + if (lg_fail(ret)) + goto fail; + + val &= ~(1 << bit); + val |= (onoff & 1) << bit; + + ret = lg216x_write_reg(state, reg, val); + lg_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_i2c_gate_ctrl(struct dvb_frontend *fe, int enable) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + if (state->cfg->deny_i2c_rptr) + return 0; + + lg_dbg("(%d)\n", enable); + + ret = lg216x_set_reg_bit(state, 0x0000, 0, enable ? 0 : 1); + + msleep(1); + + return ret; +} + +static int lg216x_soft_reset(struct lg216x_state *state) +{ + int ret; + + lg_dbg("\n"); + + ret = lg216x_write_reg(state, 0x0002, 0x00); + if (lg_fail(ret)) + goto fail; + + msleep(20); + ret = lg216x_write_reg(state, 0x0002, 0x01); + if (lg_fail(ret)) + goto fail; + + state->last_reset = jiffies_to_msecs(jiffies); +fail: + return ret; +} + +static int lg216x_initialize(struct lg216x_state *state) +{ + int ret; + + static struct lg216x_reg lg2160_init[] = { +#if 0 + { .reg = 0x0015, .val = 0xe6 }, +#else + { .reg = 0x0015, .val = 0xf7 }, + { .reg = 0x001b, .val = 0x52 }, + { .reg = 0x0208, .val = 0x00 }, + { .reg = 0x0209, .val = 0x82 }, + { .reg = 0x0210, .val = 0xf9 }, + { .reg = 0x020a, .val = 0x00 }, + { .reg = 0x020b, .val = 0x82 }, + { .reg = 0x020d, .val = 0x28 }, + { .reg = 0x020f, .val = 0x14 }, +#endif + }; + + static struct lg216x_reg lg2161_init[] = { + { .reg = 0x0000, .val = 0x41 }, + { .reg = 0x0001, .val = 0xfb }, + { .reg = 0x0216, .val = 0x00 }, + { .reg = 0x0219, .val = 0x00 }, + { .reg = 0x021b, .val = 0x55 }, + { .reg = 0x0606, .val = 0x0a }, + }; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_write_regs(state, + lg2160_init, ARRAY_SIZE(lg2160_init)); + break; + case LG2161: + ret = lg216x_write_regs(state, + lg2161_init, ARRAY_SIZE(lg2161_init)); + break; + default: + ret = -EINVAL; + break; + } + if (lg_fail(ret)) + goto fail; + + ret = lg216x_soft_reset(state); + lg_fail(ret); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_set_if(struct lg216x_state *state) +{ + u8 val; + int ret; + + lg_dbg("%d KHz\n", state->cfg->if_khz); + + ret = lg216x_read_reg(state, 0x0132, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfb; + val |= (0 == state->cfg->if_khz) ? 0x04 : 0x00; + + ret = lg216x_write_reg(state, 0x0132, val); + lg_fail(ret); + + /* if NOT zero IF, 6 MHz is the default */ +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_agc_fix(struct lg216x_state *state, + int if_agc_fix, int rf_agc_fix) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xf3; + val |= (if_agc_fix) ? 0x08 : 0x00; + val |= (rf_agc_fix) ? 0x04 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} + +#if 0 +static int lg2160_agc_freeze(struct lg216x_state *state, + int if_agc_freeze, int rf_agc_freeze) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xcf; + val |= (if_agc_freeze) ? 0x20 : 0x00; + val |= (rf_agc_freeze) ? 0x10 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} +#endif + +static int lg2160_agc_polarity(struct lg216x_state *state, + int if_agc_polarity, int rf_agc_polarity) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0100, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfc; + val |= (if_agc_polarity) ? 0x02 : 0x00; + val |= (rf_agc_polarity) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, 0x0100, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_tuner_pwr_save_polarity(struct lg216x_state *state, + int polarity) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0008, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfe; + val |= (polarity) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, 0x0008, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_spectrum_polarity(struct lg216x_state *state, + int inverted) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0132, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfd; + val |= (inverted) ? 0x02 : 0x00; + + ret = lg216x_write_reg(state, 0x0132, val); + lg_fail(ret); +fail: + return lg216x_soft_reset(state); +} + +static int lg2160_tuner_pwr_save(struct lg216x_state *state, int onoff) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0007, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xbf; + val |= (onoff) ? 0x40 : 0x00; + + ret = lg216x_write_reg(state, 0x0007, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg216x_set_parade(struct lg216x_state *state, int id) +{ + int ret; + + ret = lg216x_write_reg(state, 0x013e, id & 0x7f); + if (lg_fail(ret)) + goto fail; + + state->parade_id = id & 0x7f; +fail: + return ret; +} + +static int lg216x_set_ensemble(struct lg216x_state *state, int id) +{ + int ret; + u16 reg; + u8 val; + + switch (state->cfg->lg_chip) { + case LG2160: + reg = 0x0400; + break; + case LG2161: + default: + reg = 0x0500; + break; + } + + ret = lg216x_read_reg(state, reg, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xfe; + val |= (id) ? 0x01 : 0x00; + + ret = lg216x_write_reg(state, reg, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2160_set_spi_clock(struct lg216x_state *state) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0014, &val); + if (lg_fail(ret)) + goto fail; + + val &= 0xf3; + val |= (state->cfg->spi_clock << 2); + + ret = lg216x_write_reg(state, 0x0014, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg2161_set_output_interface(struct lg216x_state *state) +{ + u8 val; + int ret; + + ret = lg216x_read_reg(state, 0x0014, &val); + if (lg_fail(ret)) + goto fail; + + val &= ~0x07; + val |= state->cfg->output_if; /* FIXME: needs sanity check */ + + ret = lg216x_write_reg(state, 0x0014, val); + lg_fail(ret); +fail: + return ret; +} + +static int lg216x_enable_fic(struct lg216x_state *state, int onoff) +{ + int ret; + + ret = lg216x_write_reg(state, 0x0017, 0x23); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_write_reg(state, 0x0016, 0xfc); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_write_reg(state, 0x0016, + 0xfc | ((onoff) ? 0x02 : 0x00)); + break; + case LG2161: + ret = lg216x_write_reg(state, 0x0016, (onoff) ? 0x10 : 0x00); + break; + } + if (lg_fail(ret)) + goto fail; + + ret = lg216x_initialize(state); + if (lg_fail(ret)) + goto fail; + + if (onoff) { + ret = lg216x_write_reg(state, 0x0017, 0x03); + lg_fail(ret); + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_fic_version(struct lg216x_state *state, u8 *ficver) +{ + u8 val; + int ret; + + *ficver = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0128, &val); + if (lg_fail(ret)) + goto fail; + + *ficver = (val >> 3) & 0x1f; +fail: + return ret; +} + +#if 0 +static int lg2160_get_parade_id(struct lg216x_state *state, u8 *id) +{ + u8 val; + int ret; + + *id = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0123, &val); + if (lg_fail(ret)) + goto fail; + + *id = val & 0x7f; +fail: + return ret; +} +#endif + +static int lg216x_get_nog(struct lg216x_state *state, u8 *nog) +{ + u8 val; + int ret; + + *nog = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0124, &val); + if (lg_fail(ret)) + goto fail; + + *nog = ((val >> 4) & 0x07) + 1; +fail: + return ret; +} + +static int lg216x_get_tnog(struct lg216x_state *state, u8 *tnog) +{ + u8 val; + int ret; + + *tnog = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0125, &val); + if (lg_fail(ret)) + goto fail; + + *tnog = val & 0x1f; +fail: + return ret; +} + +static int lg216x_get_sgn(struct lg216x_state *state, u8 *sgn) +{ + u8 val; + int ret; + + *sgn = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0124, &val); + if (lg_fail(ret)) + goto fail; + + *sgn = val & 0x0f; +fail: + return ret; +} + +static int lg216x_get_prc(struct lg216x_state *state, u8 *prc) +{ + u8 val; + int ret; + + *prc = 0xff; /* invalid value */ + + ret = lg216x_read_reg(state, 0x0125, &val); + if (lg_fail(ret)) + goto fail; + + *prc = ((val >> 5) & 0x07) + 1; +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_rs_frame_mode(struct lg216x_state *state, + enum atscmh_rs_frame_mode *rs_framemode) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0410, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0513, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch ((val >> 4) & 0x03) { +#if 1 + default: +#endif + case 0x00: + *rs_framemode = ATSCMH_RSFRAME_PRI_ONLY; + break; + case 0x01: + *rs_framemode = ATSCMH_RSFRAME_PRI_SEC; + break; +#if 0 + default: + *rs_framemode = ATSCMH_RSFRAME_RES; + break; +#endif + } +fail: + return ret; +} + +static +int lg216x_get_rs_frame_ensemble(struct lg216x_state *state, + enum atscmh_rs_frame_ensemble *rs_frame_ens) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0400, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0500, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + val &= 0x01; + *rs_frame_ens = (enum atscmh_rs_frame_ensemble) val; +fail: + return ret; +} + +static int lg216x_get_rs_code_mode(struct lg216x_state *state, + enum atscmh_rs_code_mode *rs_code_pri, + enum atscmh_rs_code_mode *rs_code_sec) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0410, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0513, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + *rs_code_pri = (enum atscmh_rs_code_mode) ((val >> 2) & 0x03); + *rs_code_sec = (enum atscmh_rs_code_mode) (val & 0x03); +fail: + return ret; +} + +static int lg216x_get_sccc_block_mode(struct lg216x_state *state, + enum atscmh_sccc_block_mode *sccc_block) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0315, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0511, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch (val & 0x03) { + case 0x00: + *sccc_block = ATSCMH_SCCC_BLK_SEP; + break; + case 0x01: + *sccc_block = ATSCMH_SCCC_BLK_COMB; + break; + default: + *sccc_block = ATSCMH_SCCC_BLK_RES; + break; + } +fail: + return ret; +} + +static int lg216x_get_sccc_code_mode(struct lg216x_state *state, + enum atscmh_sccc_code_mode *mode_a, + enum atscmh_sccc_code_mode *mode_b, + enum atscmh_sccc_code_mode *mode_c, + enum atscmh_sccc_code_mode *mode_d) +{ + u8 val; + int ret; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0316, &val); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x0512, &val); + break; + default: + ret = -EINVAL; + } + if (lg_fail(ret)) + goto fail; + + switch ((val >> 6) & 0x03) { + case 0x00: + *mode_a = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_a = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_a = ATSCMH_SCCC_CODE_RES; + break; + } + + switch ((val >> 4) & 0x03) { + case 0x00: + *mode_b = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_b = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_b = ATSCMH_SCCC_CODE_RES; + break; + } + + switch ((val >> 2) & 0x03) { + case 0x00: + *mode_c = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_c = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_c = ATSCMH_SCCC_CODE_RES; + break; + } + + switch (val & 0x03) { + case 0x00: + *mode_d = ATSCMH_SCCC_CODE_HLF; + break; + case 0x01: + *mode_d = ATSCMH_SCCC_CODE_QTR; + break; + default: + *mode_d = ATSCMH_SCCC_CODE_RES; + break; + } +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +#if 0 +static int lg216x_read_fic_err_count(struct lg216x_state *state, u8 *err) +{ + u8 fic_err; + int ret; + + *err = 0; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg216x_read_reg(state, 0x0012, &fic_err); + break; + case LG2161: + ret = lg216x_read_reg(state, 0x001e, &fic_err); + break; + } + if (lg_fail(ret)) + goto fail; + + *err = fic_err; +fail: + return ret; +} + +static int lg2160_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + u8 crc_err1, crc_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0411, &crc_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0412, &crc_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)(((crc_err2 & 0x0f) << 8) | crc_err1); +fail: + return ret; +} + +static int lg2161_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + u8 crc_err; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0612, &crc_err); + if (lg_fail(ret)) + goto fail; + + *err = (u16)crc_err; +fail: + return ret; +} + +static int lg216x_read_crc_err_count(struct lg216x_state *state, u16 *err) +{ + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_crc_err_count(state, err); + break; + case LG2161: + ret = lg2161_read_crc_err_count(state, err); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int lg2160_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + u8 rs_err1, rs_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0413, &rs_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0414, &rs_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)(((rs_err2 & 0x0f) << 8) | rs_err1); +fail: + return ret; +} + +static int lg2161_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + u8 rs_err1, rs_err2; + int ret; + + *err = 0; + + ret = lg216x_read_reg(state, 0x0613, &rs_err1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0614, &rs_err2); + if (lg_fail(ret)) + goto fail; + + *err = (u16)((rs_err1 << 8) | rs_err2); +fail: + return ret; +} + +static int lg216x_read_rs_err_count(struct lg216x_state *state, u16 *err) +{ + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_rs_err_count(state, err); + break; + case LG2161: + ret = lg2161_read_rs_err_count(state, err); + break; + default: + ret = -EINVAL; + break; + } + return ret; +} +#endif + +/* ------------------------------------------------------------------------ */ + +static int lg216x_get_frontend(struct dvb_frontend *fe) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("\n"); + + fe->dtv_property_cache.modulation = VSB_8; + fe->dtv_property_cache.frequency = state->current_frequency; + fe->dtv_property_cache.delivery_system = SYS_ATSCMH; + + ret = lg216x_get_fic_version(state, + &fe->dtv_property_cache.atscmh_fic_ver); + if (lg_fail(ret)) + goto fail; + if (state->fic_ver != fe->dtv_property_cache.atscmh_fic_ver) { + state->fic_ver = fe->dtv_property_cache.atscmh_fic_ver; + +#if 0 + ret = lg2160_get_parade_id(state, + &fe->dtv_property_cache.atscmh_parade_id); + if (lg_fail(ret)) + goto fail; +/* #else */ + fe->dtv_property_cache.atscmh_parade_id = state->parade_id; +#endif + ret = lg216x_get_nog(state, + &fe->dtv_property_cache.atscmh_nog); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_tnog(state, + &fe->dtv_property_cache.atscmh_tnog); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sgn(state, + &fe->dtv_property_cache.atscmh_sgn); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_prc(state, + &fe->dtv_property_cache.atscmh_prc); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_get_rs_frame_mode(state, + (enum atscmh_rs_frame_mode *) + &fe->dtv_property_cache.atscmh_rs_frame_mode); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_rs_frame_ensemble(state, + (enum atscmh_rs_frame_ensemble *) + &fe->dtv_property_cache.atscmh_rs_frame_ensemble); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_rs_code_mode(state, + (enum atscmh_rs_code_mode *) + &fe->dtv_property_cache.atscmh_rs_code_mode_pri, + (enum atscmh_rs_code_mode *) + &fe->dtv_property_cache.atscmh_rs_code_mode_sec); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sccc_block_mode(state, + (enum atscmh_sccc_block_mode *) + &fe->dtv_property_cache.atscmh_sccc_block_mode); + if (lg_fail(ret)) + goto fail; + ret = lg216x_get_sccc_code_mode(state, + (enum atscmh_sccc_code_mode *) + &fe->dtv_property_cache.atscmh_sccc_code_mode_a, + (enum atscmh_sccc_code_mode *) + &fe->dtv_property_cache.atscmh_sccc_code_mode_b, + (enum atscmh_sccc_code_mode *) + &fe->dtv_property_cache.atscmh_sccc_code_mode_c, + (enum atscmh_sccc_code_mode *) + &fe->dtv_property_cache.atscmh_sccc_code_mode_d); + if (lg_fail(ret)) + goto fail; + } +#if 0 + ret = lg216x_read_fic_err_count(state, + (u8 *)&fe->dtv_property_cache.atscmh_fic_err); + if (lg_fail(ret)) + goto fail; + ret = lg216x_read_crc_err_count(state, + &fe->dtv_property_cache.atscmh_crc_err); + if (lg_fail(ret)) + goto fail; + ret = lg216x_read_rs_err_count(state, + &fe->dtv_property_cache.atscmh_rs_err); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + if (((fe->dtv_property_cache.atscmh_rs_err >= 240) && + (fe->dtv_property_cache.atscmh_crc_err >= 240)) && + ((jiffies_to_msecs(jiffies) - state->last_reset) > 6000)) + ret = lg216x_soft_reset(state); + break; + case LG2161: + /* no fix needed here (as far as we know) */ + ret = 0; + break; + } + lg_fail(ret); +#endif +fail: + return ret; +} + +static int lg216x_get_property(struct dvb_frontend *fe, + struct dtv_property *tvp) +{ + return (DTV_ATSCMH_FIC_VER == tvp->cmd) ? + lg216x_get_frontend(fe) : 0; +} + + +static int lg2160_set_frontend(struct dvb_frontend *fe) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + lg_dbg("(%d)\n", fe->dtv_property_cache.frequency); + + if (fe->ops.tuner_ops.set_params) { + ret = fe->ops.tuner_ops.set_params(fe); + if (fe->ops.i2c_gate_ctrl) + fe->ops.i2c_gate_ctrl(fe, 0); + if (lg_fail(ret)) + goto fail; + state->current_frequency = fe->dtv_property_cache.frequency; + } + + ret = lg2160_agc_fix(state, 0, 0); + if (lg_fail(ret)) + goto fail; + ret = lg2160_agc_polarity(state, 0, 0); + if (lg_fail(ret)) + goto fail; + ret = lg2160_tuner_pwr_save_polarity(state, 1); + if (lg_fail(ret)) + goto fail; + ret = lg216x_set_if(state); + if (lg_fail(ret)) + goto fail; + ret = lg2160_spectrum_polarity(state, state->cfg->spectral_inversion); + if (lg_fail(ret)) + goto fail; + + /* be tuned before this point */ + ret = lg216x_soft_reset(state); + if (lg_fail(ret)) + goto fail; + + ret = lg2160_tuner_pwr_save(state, 0); + if (lg_fail(ret)) + goto fail; + + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_set_spi_clock(state); + if (lg_fail(ret)) + goto fail; + break; + case LG2161: + ret = lg2161_set_output_interface(state); + if (lg_fail(ret)) + goto fail; + break; + } + + ret = lg216x_set_parade(state, fe->dtv_property_cache.atscmh_parade_id); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_set_ensemble(state, + fe->dtv_property_cache.atscmh_rs_frame_ensemble); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_initialize(state); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_enable_fic(state, 1); + lg_fail(ret); + + lg216x_get_frontend(fe); +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ + u8 val; + int ret; + + *acq_lock = 0; + *sync_lock = 0; + + ret = lg216x_read_reg(state, 0x011b, &val); + if (lg_fail(ret)) + goto fail; + + *sync_lock = (val & 0x20) ? 0 : 1; + *acq_lock = (val & 0x40) ? 0 : 1; +fail: + return ret; +} + +#ifdef USE_LG2161_LOCK_BITS +static int lg2161_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ + u8 val; + int ret; + + *acq_lock = 0; + *sync_lock = 0; + + ret = lg216x_read_reg(state, 0x0304, &val); + if (lg_fail(ret)) + goto fail; + + *sync_lock = (val & 0x80) ? 0 : 1; + + ret = lg216x_read_reg(state, 0x011b, &val); + if (lg_fail(ret)) + goto fail; + + *acq_lock = (val & 0x40) ? 0 : 1; +fail: + return ret; +} +#endif + +static int lg216x_read_lock_status(struct lg216x_state *state, + int *acq_lock, int *sync_lock) +{ +#ifdef USE_LG2161_LOCK_BITS + int ret; + switch (state->cfg->lg_chip) { + case LG2160: + ret = lg2160_read_lock_status(state, acq_lock, sync_lock); + break; + case LG2161: + ret = lg2161_read_lock_status(state, acq_lock, sync_lock); + break; + default: + ret = -EINVAL; + break; + } + return ret; +#else + return lg2160_read_lock_status(state, acq_lock, sync_lock); +#endif +} + +static int lg216x_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + struct lg216x_state *state = fe->demodulator_priv; + int ret, acq_lock, sync_lock; + + *status = 0; + + ret = lg216x_read_lock_status(state, &acq_lock, &sync_lock); + if (lg_fail(ret)) + goto fail; + + lg_dbg("%s%s\n", + acq_lock ? "SIGNALEXIST " : "", + sync_lock ? "SYNCLOCK" : ""); + + if (acq_lock) + *status |= FE_HAS_SIGNAL; + if (sync_lock) + *status |= FE_HAS_SYNC; + + if (*status) + *status |= FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_LOCK; + +fail: + return ret; +} + +/* ------------------------------------------------------------------------ */ + +static int lg2160_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lg216x_state *state = fe->demodulator_priv; + u8 snr1, snr2; + int ret; + + *snr = 0; + + ret = lg216x_read_reg(state, 0x0202, &snr1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0203, &snr2); + if (lg_fail(ret)) + goto fail; + + if ((snr1 == 0xba) || (snr2 == 0xdf)) + *snr = 0; + else +#if 1 + *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 >> 4); +#else /* BCD */ + *snr = (snr2 | (snr1 << 8)); +#endif +fail: + return ret; +} + +static int lg2161_read_snr(struct dvb_frontend *fe, u16 *snr) +{ + struct lg216x_state *state = fe->demodulator_priv; + u8 snr1, snr2; + int ret; + + *snr = 0; + + ret = lg216x_read_reg(state, 0x0302, &snr1); + if (lg_fail(ret)) + goto fail; + + ret = lg216x_read_reg(state, 0x0303, &snr2); + if (lg_fail(ret)) + goto fail; + + if ((snr1 == 0xba) || (snr2 == 0xfd)) + *snr = 0; + else + + *snr = ((snr1 >> 4) * 100) + ((snr1 & 0x0f) * 10) + (snr2 & 0x0f); +fail: + return ret; +} + +static int lg216x_read_signal_strength(struct dvb_frontend *fe, + u16 *strength) +{ +#if 0 + /* borrowed from lgdt330x.c + * + * Calculate strength from SNR up to 35dB + * Even though the SNR can go higher than 35dB, + * there is some comfort factor in having a range of + * strong signals that can show at 100% + */ + struct lg216x_state *state = fe->demodulator_priv; + u16 snr; + int ret; +#endif + *strength = 0; +#if 0 + ret = fe->ops.read_snr(fe, &snr); + if (lg_fail(ret)) + goto fail; + /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */ + /* scale the range 0 - 35*2^24 into 0 - 65535 */ + if (state->snr >= 8960 * 0x10000) + *strength = 0xffff; + else + *strength = state->snr / 8960; +fail: + return ret; +#else + return 0; +#endif +} + +/* ------------------------------------------------------------------------ */ + +static int lg216x_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) +{ +#if 0 + struct lg216x_state *state = fe->demodulator_priv; + int ret; + + ret = lg216x_read_rs_err_count(state, + &fe->dtv_property_cache.atscmh_rs_err); + if (lg_fail(ret)) + goto fail; + + *ucblocks = fe->dtv_property_cache.atscmh_rs_err; +fail: +#else + *ucblocks = 0; +#endif + return 0; +} + +static int lg216x_get_tune_settings(struct dvb_frontend *fe, + struct dvb_frontend_tune_settings + *fe_tune_settings) +{ + fe_tune_settings->min_delay_ms = 500; + lg_dbg("\n"); + return 0; +} + +static void lg216x_release(struct dvb_frontend *fe) +{ + struct lg216x_state *state = fe->demodulator_priv; + lg_dbg("\n"); + kfree(state); +} + +static struct dvb_frontend_ops lg2160_ops = { + .delsys = { SYS_ATSCMH }, + .info = { + .name = "LG Electronics LG2160 ATSC/MH Frontend", + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + }, + .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, +#if 0 + .init = lg216x_init, + .sleep = lg216x_sleep, +#endif + .get_property = lg216x_get_property, + + .set_frontend = lg2160_set_frontend, + .get_frontend = lg216x_get_frontend, + .get_tune_settings = lg216x_get_tune_settings, + .read_status = lg216x_read_status, +#if 0 + .read_ber = lg216x_read_ber, +#endif + .read_signal_strength = lg216x_read_signal_strength, + .read_snr = lg2160_read_snr, + .read_ucblocks = lg216x_read_ucblocks, + .release = lg216x_release, +}; + +static struct dvb_frontend_ops lg2161_ops = { + .delsys = { SYS_ATSCMH }, + .info = { + .name = "LG Electronics LG2161 ATSC/MH Frontend", + .frequency_min = 54000000, + .frequency_max = 858000000, + .frequency_stepsize = 62500, + }, + .i2c_gate_ctrl = lg216x_i2c_gate_ctrl, +#if 0 + .init = lg216x_init, + .sleep = lg216x_sleep, +#endif + .get_property = lg216x_get_property, + + .set_frontend = lg2160_set_frontend, + .get_frontend = lg216x_get_frontend, + .get_tune_settings = lg216x_get_tune_settings, + .read_status = lg216x_read_status, +#if 0 + .read_ber = lg216x_read_ber, +#endif + .read_signal_strength = lg216x_read_signal_strength, + .read_snr = lg2161_read_snr, + .read_ucblocks = lg216x_read_ucblocks, + .release = lg216x_release, +}; + +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap) +{ + struct lg216x_state *state = NULL; + + lg_dbg("(%d-%04x)\n", + i2c_adap ? i2c_adapter_id(i2c_adap) : 0, + config ? config->i2c_addr : 0); + + state = kzalloc(sizeof(struct lg216x_state), GFP_KERNEL); + if (state == NULL) + goto fail; + + state->cfg = config; + state->i2c_adap = i2c_adap; + state->fic_ver = 0xff; + state->parade_id = 0xff; + + switch (config->lg_chip) { + default: + lg_warn("invalid chip requested, defaulting to LG2160"); + /* fall-thru */ + case LG2160: + memcpy(&state->frontend.ops, &lg2160_ops, + sizeof(struct dvb_frontend_ops)); + break; + case LG2161: + memcpy(&state->frontend.ops, &lg2161_ops, + sizeof(struct dvb_frontend_ops)); + break; + } + + state->frontend.demodulator_priv = state; + state->current_frequency = -1; + /* parade 1 by default */ + state->frontend.dtv_property_cache.atscmh_parade_id = 1; + + return &state->frontend; +fail: + lg_warn("unable to detect LG216x hardware\n"); + kfree(state); + return NULL; +} +EXPORT_SYMBOL(lg2160_attach); + +MODULE_DESCRIPTION("LG Electronics LG216x ATSC/MH Demodulator Driver"); +MODULE_AUTHOR("Michael Krufky "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.3"); + +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/drivers/media/dvb/frontends/lg2160.h b/drivers/media/dvb/frontends/lg2160.h new file mode 100644 index 000000000000..9e2c0f41199a --- /dev/null +++ b/drivers/media/dvb/frontends/lg2160.h @@ -0,0 +1,84 @@ +/* + * Support for LG2160 - ATSC/MH + * + * Copyright (C) 2010 Michael Krufky + * + * 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 _LG2160_H_ +#define _LG2160_H_ + +#include +#include "dvb_frontend.h" + +enum lg_chip_type { + LG2160 = 0, + LG2161 = 1, +}; + +#define LG2161_1019 LG2161 +#define LG2161_1040 LG2161 + +enum lg2160_spi_clock { + LG2160_SPI_3_125_MHZ = 0, + LG2160_SPI_6_25_MHZ = 1, + LG2160_SPI_12_5_MHZ = 2, +}; + +#if 0 +enum lg2161_oif { + LG2161_OIF_EBI2_SLA = 1, + LG2161_OIF_SDIO_SLA = 2, + LG2161_OIF_SPI_SLA = 3, + LG2161_OIF_SPI_MAS = 4, + LG2161_OIF_SERIAL_TS = 7, +}; +#endif + +struct lg2160_config { + u8 i2c_addr; + + /* user defined IF frequency in KHz */ + u16 if_khz; + + /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */ + int deny_i2c_rptr:1; + + /* spectral inversion - 0:disabled 1:enabled */ + int spectral_inversion:1; + + unsigned int output_if; + enum lg2160_spi_clock spi_clock; + enum lg_chip_type lg_chip; +}; + +#if defined(CONFIG_DVB_LG2160) || (defined(CONFIG_DVB_LG2160_MODULE) && \ + defined(MODULE)) +extern +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap); +#else +static inline +struct dvb_frontend *lg2160_attach(const struct lg2160_config *config, + struct i2c_adapter *i2c_adap) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif /* CONFIG_DVB_LG2160 */ + +#endif /* _LG2160_H_ */ diff --git a/drivers/media/dvb/frontends/lgs8gxx.c b/drivers/media/dvb/frontends/lgs8gxx.c index 4de1d3520cd2..568363a10a31 100644 --- a/drivers/media/dvb/frontends/lgs8gxx.c +++ b/drivers/media/dvb/frontends/lgs8gxx.c @@ -262,7 +262,6 @@ static int lgs8gxx_set_mode_auto(struct lgs8gxx_state *priv) static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) { - int ret = 0; u8 t; if (priv->config->prod == LGS8GXX_PROD_LGS8G75) { @@ -296,7 +295,7 @@ static int lgs8gxx_set_mode_manual(struct lgs8gxx_state *priv) if (priv->config->prod == LGS8GXX_PROD_LGS8913) lgs8gxx_write_reg(priv, 0xC1, 0); - ret = lgs8gxx_read_reg(priv, 0xC5, &t); + lgs8gxx_read_reg(priv, 0xC5, &t); t = (t & 0xE0) | 0x06; lgs8gxx_write_reg(priv, 0xC5, t); diff --git a/drivers/media/dvb/frontends/m88rs2000.c b/drivers/media/dvb/frontends/m88rs2000.c index 045ee5a6f7ae..312588e84dae 100644 --- a/drivers/media/dvb/frontends/m88rs2000.c +++ b/drivers/media/dvb/frontends/m88rs2000.c @@ -416,9 +416,25 @@ static int m88rs2000_tab_set(struct m88rs2000_state *state, static int m88rs2000_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t volt) { - deb_info("%s: %s\n", __func__, - volt == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" : - volt == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??"); + struct m88rs2000_state *state = fe->demodulator_priv; + u8 data; + + data = m88rs2000_demod_read(state, 0xb2); + data |= 0x03; /* bit0 V/H, bit1 off/on */ + + switch (volt) { + case SEC_VOLTAGE_18: + data &= ~0x03; + break; + case SEC_VOLTAGE_13: + data &= ~0x03; + data |= 0x01; + break; + case SEC_VOLTAGE_OFF: + break; + } + + m88rs2000_demod_write(state, 0xb2, data); return 0; } @@ -654,7 +670,6 @@ static int m88rs2000_set_tuner(struct dvb_frontend *fe, u16 *offset) static int m88rs2000_set_fec(struct m88rs2000_state *state, fe_code_rate_t fec) { - int ret; u16 fec_set; switch (fec) { /* This is not confirmed kept for reference */ @@ -677,7 +692,7 @@ static int m88rs2000_set_fec(struct m88rs2000_state *state, default: fec_set = 0x08; } - ret = m88rs2000_demod_write(state, 0x76, fec_set); + m88rs2000_demod_write(state, 0x76, fec_set); return 0; } @@ -772,13 +787,13 @@ static int m88rs2000_set_frontend(struct dvb_frontend *fe) return -ENODEV; for (i = 0; i < 25; i++) { - u8 reg = m88rs2000_demod_read(state, 0x8c); + reg = m88rs2000_demod_read(state, 0x8c); if ((reg & 0x7) == 0x7) { status = FE_HAS_LOCK; break; } state->no_lock_count++; - if (state->no_lock_count > 15) { + if (state->no_lock_count == 15) { reg = m88rs2000_demod_read(state, 0x70); reg ^= 0x4; m88rs2000_demod_write(state, 0x70, reg); diff --git a/drivers/media/dvb/frontends/rtl2830.c b/drivers/media/dvb/frontends/rtl2830.c index 45196c5b0736..93612ebac519 100644 --- a/drivers/media/dvb/frontends/rtl2830.c +++ b/drivers/media/dvb/frontends/rtl2830.c @@ -374,6 +374,118 @@ err: return ret; } +static int rtl2830_get_frontend(struct dvb_frontend *fe) +{ + struct rtl2830_priv *priv = fe->demodulator_priv; + struct dtv_frontend_properties *c = &fe->dtv_property_cache; + int ret; + u8 buf[3]; + + if (priv->sleeping) + return 0; + + ret = rtl2830_rd_regs(priv, 0x33c, buf, 2); + if (ret) + goto err; + + ret = rtl2830_rd_reg(priv, 0x351, &buf[2]); + if (ret) + goto err; + + dbg("%s: TPS=%02x %02x %02x", __func__, buf[0], buf[1], buf[2]); + + switch ((buf[0] >> 2) & 3) { + case 0: + c->modulation = QPSK; + break; + case 1: + c->modulation = QAM_16; + break; + case 2: + c->modulation = QAM_64; + break; + } + + switch ((buf[2] >> 2) & 1) { + case 0: + c->transmission_mode = TRANSMISSION_MODE_2K; + break; + case 1: + c->transmission_mode = TRANSMISSION_MODE_8K; + } + + switch ((buf[2] >> 0) & 3) { + case 0: + c->guard_interval = GUARD_INTERVAL_1_32; + break; + case 1: + c->guard_interval = GUARD_INTERVAL_1_16; + break; + case 2: + c->guard_interval = GUARD_INTERVAL_1_8; + break; + case 3: + c->guard_interval = GUARD_INTERVAL_1_4; + break; + } + + switch ((buf[0] >> 4) & 7) { + case 0: + c->hierarchy = HIERARCHY_NONE; + break; + case 1: + c->hierarchy = HIERARCHY_1; + break; + case 2: + c->hierarchy = HIERARCHY_2; + break; + case 3: + c->hierarchy = HIERARCHY_4; + break; + } + + switch ((buf[1] >> 3) & 7) { + case 0: + c->code_rate_HP = FEC_1_2; + break; + case 1: + c->code_rate_HP = FEC_2_3; + break; + case 2: + c->code_rate_HP = FEC_3_4; + break; + case 3: + c->code_rate_HP = FEC_5_6; + break; + case 4: + c->code_rate_HP = FEC_7_8; + break; + } + + switch ((buf[1] >> 0) & 7) { + case 0: + c->code_rate_LP = FEC_1_2; + break; + case 1: + c->code_rate_LP = FEC_2_3; + break; + case 2: + c->code_rate_LP = FEC_3_4; + break; + case 3: + c->code_rate_LP = FEC_5_6; + break; + case 4: + c->code_rate_LP = FEC_7_8; + break; + } + + return 0; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; +} + static int rtl2830_read_status(struct dvb_frontend *fe, fe_status_t *status) { struct rtl2830_priv *priv = fe->demodulator_priv; @@ -404,14 +516,72 @@ err: static int rtl2830_read_snr(struct dvb_frontend *fe, u16 *snr) { - *snr = 0; + struct rtl2830_priv *priv = fe->demodulator_priv; + int ret, hierarchy, constellation; + u8 buf[2], tmp; + u16 tmp16; +#define CONSTELLATION_NUM 3 +#define HIERARCHY_NUM 4 + static const u32 snr_constant[CONSTELLATION_NUM][HIERARCHY_NUM] = { + { 70705899, 70705899, 70705899, 70705899 }, + { 82433173, 82433173, 87483115, 94445660 }, + { 92888734, 92888734, 95487525, 99770748 }, + }; + + if (priv->sleeping) + return 0; + + /* reports SNR in resolution of 0.1 dB */ + + ret = rtl2830_rd_reg(priv, 0x33c, &tmp); + if (ret) + goto err; + + constellation = (tmp >> 2) & 0x03; /* [3:2] */ + if (constellation > CONSTELLATION_NUM - 1) + goto err; + + hierarchy = (tmp >> 4) & 0x07; /* [6:4] */ + if (hierarchy > HIERARCHY_NUM - 1) + goto err; + + ret = rtl2830_rd_regs(priv, 0x40c, buf, 2); + if (ret) + goto err; + + tmp16 = buf[0] << 8 | buf[1]; + + if (tmp16) + *snr = (snr_constant[constellation][hierarchy] - + intlog10(tmp16)) / ((1 << 24) / 100); + else + *snr = 0; + return 0; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; } static int rtl2830_read_ber(struct dvb_frontend *fe, u32 *ber) { - *ber = 0; + struct rtl2830_priv *priv = fe->demodulator_priv; + int ret; + u8 buf[2]; + + if (priv->sleeping) + return 0; + + ret = rtl2830_rd_regs(priv, 0x34e, buf, 2); + if (ret) + goto err; + + *ber = buf[0] << 8 | buf[1]; + return 0; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; } static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) @@ -422,8 +592,32 @@ static int rtl2830_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks) static int rtl2830_read_signal_strength(struct dvb_frontend *fe, u16 *strength) { - *strength = 0; + struct rtl2830_priv *priv = fe->demodulator_priv; + int ret; + u8 buf[2]; + u16 if_agc_raw, if_agc; + + if (priv->sleeping) + return 0; + + ret = rtl2830_rd_regs(priv, 0x359, buf, 2); + if (ret) + goto err; + + if_agc_raw = (buf[0] << 8 | buf[1]) & 0x3fff; + + if (if_agc_raw & (1 << 9)) + if_agc = -(~(if_agc_raw - 1) & 0x1ff); + else + if_agc = if_agc_raw; + + *strength = (u8) (55 - if_agc / 182); + *strength |= *strength << 8; + return 0; +err: + dbg("%s: failed=%d", __func__, ret); + return ret; } static struct dvb_frontend_ops rtl2830_ops; @@ -549,6 +743,7 @@ static struct dvb_frontend_ops rtl2830_ops = { .get_tune_settings = rtl2830_get_tune_settings, .set_frontend = rtl2830_set_frontend, + .get_frontend = rtl2830_get_frontend, .read_status = rtl2830_read_status, .read_snr = rtl2830_read_snr, diff --git a/drivers/media/dvb/frontends/rtl2830_priv.h b/drivers/media/dvb/frontends/rtl2830_priv.h index 4a464761b5b8..9b20557ccf6c 100644 --- a/drivers/media/dvb/frontends/rtl2830_priv.h +++ b/drivers/media/dvb/frontends/rtl2830_priv.h @@ -22,6 +22,7 @@ #define RTL2830_PRIV_H #include "dvb_frontend.h" +#include "dvb_math.h" #include "rtl2830.h" #define LOG_PREFIX "rtl2830" diff --git a/drivers/media/dvb/frontends/stb0899_drv.c b/drivers/media/dvb/frontends/stb0899_drv.c index dd08f4ac64a8..8b0dc74a3298 100644 --- a/drivers/media/dvb/frontends/stb0899_drv.c +++ b/drivers/media/dvb/frontends/stb0899_drv.c @@ -637,11 +637,9 @@ static void stb0899_init_calc(struct stb0899_state *state) struct stb0899_internal *internal = &state->internal; int master_clk; u8 agc[2]; - u8 agc1cn; u32 reg; /* Read registers (in burst mode) */ - agc1cn = stb0899_read_reg(state, STB0899_AGC1CN); stb0899_read_regs(state, STB0899_AGC1REF, agc, 2); /* AGC1R and AGC2O */ /* Initial calculations */ @@ -823,15 +821,12 @@ static int stb0899_send_diseqc_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t static int stb0899_diseqc_init(struct stb0899_state *state) { - struct dvb_diseqc_master_cmd tx_data; /* struct dvb_diseqc_slave_reply rx_data; */ - u8 f22_tx, f22_rx, reg; + u8 f22_tx, reg; u32 mclk, tx_freq = 22000;/* count = 0, i; */ - tx_data.msg[0] = 0xe2; - tx_data.msg_len = 3; reg = stb0899_read_reg(state, STB0899_DISCNTRL2); STB0899_SETFIELD_VAL(ONECHIP_TRX, reg, 0); stb0899_write_reg(state, STB0899_DISCNTRL2, reg); @@ -849,7 +844,6 @@ static int stb0899_diseqc_init(struct stb0899_state *state) f22_tx = mclk / (tx_freq * 32); stb0899_write_reg(state, STB0899_DISF22, f22_tx); /* DiSEqC Tx freq */ state->rx_freq = 20000; - f22_rx = mclk / (state->rx_freq * 32); return 0; } diff --git a/drivers/media/dvb/frontends/stb6100.c b/drivers/media/dvb/frontends/stb6100.c index def88abb30bf..2e93e65d2cdb 100644 --- a/drivers/media/dvb/frontends/stb6100.c +++ b/drivers/media/dvb/frontends/stb6100.c @@ -158,7 +158,6 @@ static int stb6100_read_regs(struct stb6100_state *state, u8 regs[]) static int stb6100_read_reg(struct stb6100_state *state, u8 reg) { u8 regs[STB6100_NUMREGS]; - int rc; struct i2c_msg msg = { .addr = state->config->tuner_address + reg, @@ -167,7 +166,7 @@ static int stb6100_read_reg(struct stb6100_state *state, u8 reg) .len = 1 }; - rc = i2c_transfer(state->i2c, &msg, 1); + i2c_transfer(state->i2c, &msg, 1); if (unlikely(reg >= STB6100_NUMREGS)) { dprintk(verbose, FE_ERROR, 1, "Invalid register offset 0x%x", reg); diff --git a/drivers/media/dvb/frontends/stv0297.c b/drivers/media/dvb/frontends/stv0297.c index 85c157a1fe5e..d40f226160ef 100644 --- a/drivers/media/dvb/frontends/stv0297.c +++ b/drivers/media/dvb/frontends/stv0297.c @@ -414,7 +414,6 @@ static int stv0297_set_frontend(struct dvb_frontend *fe) int delay; int sweeprate; int carrieroffset; - unsigned long starttime; unsigned long timeout; fe_spectral_inversion_t inversion; @@ -543,7 +542,6 @@ static int stv0297_set_frontend(struct dvb_frontend *fe) stv0297_writereg_mask(state, 0x43, 0x10, 0x10); /* wait for WGAGC lock */ - starttime = jiffies; timeout = jiffies + msecs_to_jiffies(2000); while (time_before(jiffies, timeout)) { msleep(10); diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c index ba0709b2d433..4af20780fb9c 100644 --- a/drivers/media/dvb/frontends/stv0900_sw.c +++ b/drivers/media/dvb/frontends/stv0900_sw.c @@ -835,7 +835,6 @@ static void stv0900_track_optimization(struct dvb_frontend *fe) blind_tun_sw = 0, modulation; - enum fe_stv0900_rolloff rolloff; enum fe_stv0900_modcode foundModcod; dprintk("%s\n", __func__); @@ -940,7 +939,6 @@ static void stv0900_track_optimization(struct dvb_frontend *fe) 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); diff --git a/drivers/media/dvb/frontends/stv090x.c b/drivers/media/dvb/frontends/stv090x.c index 4aef1877ed42..d79e69f65cbb 100644 --- a/drivers/media/dvb/frontends/stv090x.c +++ b/drivers/media/dvb/frontends/stv090x.c @@ -2842,7 +2842,6 @@ static int stv090x_optimize_track(struct stv090x_state *state) { struct dvb_frontend *fe = &state->frontend; - enum stv090x_rolloff rolloff; enum stv090x_modcod modcod; s32 srate, pilots, aclc, f_1, f_0, i = 0, blind_tune = 0; @@ -2966,7 +2965,6 @@ static int stv090x_optimize_track(struct stv090x_state *state) f_1 = STV090x_READ_DEMOD(state, CFR2); f_0 = STV090x_READ_DEMOD(state, CFR1); reg = STV090x_READ_DEMOD(state, TMGOBS); - rolloff = STV090x_GETFIELD_Px(reg, ROLLOFF_STATUS_FIELD); if (state->algo == STV090x_BLIND_SEARCH) { STV090x_WRITE_DEMOD(state, SFRSTEP, 0x00); diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c index ac7237891374..82946cd517f5 100644 --- a/drivers/media/dvb/frontends/zl10353.c +++ b/drivers/media/dvb/frontends/zl10353.c @@ -525,7 +525,7 @@ static int zl10353_read_snr(struct dvb_frontend *fe, u16 *snr) zl10353_dump_regs(fe); _snr = zl10353_read_register(state, SNR); - *snr = (_snr << 8) | _snr; + *snr = 10 * _snr / 8; return 0; } @@ -559,7 +559,6 @@ static int zl10353_init(struct dvb_frontend *fe) { struct zl10353_state *state = fe->demodulator_priv; u8 zl10353_reset_attach[6] = { 0x50, 0x03, 0x64, 0x46, 0x15, 0x0F }; - int rc = 0; if (debug_regs) zl10353_dump_regs(fe); @@ -573,7 +572,7 @@ static int zl10353_init(struct dvb_frontend *fe) /* Do a "hard" reset if not already done */ if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] || zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) { - rc = zl10353_write(fe, zl10353_reset_attach, + zl10353_write(fe, zl10353_reset_attach, sizeof(zl10353_reset_attach)); if (debug_regs) zl10353_dump_regs(fe); diff --git a/drivers/media/dvb/mantis/hopper_cards.c b/drivers/media/dvb/mantis/hopper_cards.c index 71622f65c037..cc0251e01077 100644 --- a/drivers/media/dvb/mantis/hopper_cards.c +++ b/drivers/media/dvb/mantis/hopper_cards.c @@ -65,7 +65,7 @@ static int devs; static irqreturn_t hopper_irq_handler(int irq, void *dev_id) { - u32 stat = 0, mask = 0, lstat = 0; + u32 stat = 0, mask = 0; u32 rst_stat = 0, rst_mask = 0; struct mantis_pci *mantis; @@ -80,7 +80,6 @@ static irqreturn_t hopper_irq_handler(int irq, void *dev_id) stat = mmread(MANTIS_INT_STAT); mask = mmread(MANTIS_INT_MASK); - lstat = stat & ~MANTIS_INT_RISCSTAT; if (!(stat & mask)) return IRQ_NONE; diff --git a/drivers/media/dvb/mantis/mantis_cards.c b/drivers/media/dvb/mantis/mantis_cards.c index c2bb90b3e529..095cf3a994e2 100644 --- a/drivers/media/dvb/mantis/mantis_cards.c +++ b/drivers/media/dvb/mantis/mantis_cards.c @@ -73,7 +73,7 @@ static char *label[10] = { static irqreturn_t mantis_irq_handler(int irq, void *dev_id) { - u32 stat = 0, mask = 0, lstat = 0; + u32 stat = 0, mask = 0; u32 rst_stat = 0, rst_mask = 0; struct mantis_pci *mantis; @@ -88,7 +88,6 @@ static irqreturn_t mantis_irq_handler(int irq, void *dev_id) stat = mmread(MANTIS_INT_STAT); mask = mmread(MANTIS_INT_MASK); - lstat = stat & ~MANTIS_INT_RISCSTAT; if (!(stat & mask)) return IRQ_NONE; diff --git a/drivers/media/dvb/mantis/mantis_dma.c b/drivers/media/dvb/mantis/mantis_dma.c index c61ca7d3daea..566c407175a4 100644 --- a/drivers/media/dvb/mantis/mantis_dma.c +++ b/drivers/media/dvb/mantis/mantis_dma.c @@ -199,10 +199,6 @@ void mantis_dma_start(struct mantis_pci *mantis) void mantis_dma_stop(struct mantis_pci *mantis) { - u32 stat = 0, mask = 0; - - stat = mmread(MANTIS_INT_STAT); - mask = mmread(MANTIS_INT_MASK); dprintk(MANTIS_DEBUG, 1, "Mantis Stop DMA engine"); mmwrite((mmread(MANTIS_GPIF_ADDR) & (~(MANTIS_GPIF_HIFRDWRN))), MANTIS_GPIF_ADDR); diff --git a/drivers/media/dvb/mantis/mantis_evm.c b/drivers/media/dvb/mantis/mantis_evm.c index 36f2256ebb0e..71ce52875c38 100644 --- a/drivers/media/dvb/mantis/mantis_evm.c +++ b/drivers/media/dvb/mantis/mantis_evm.c @@ -41,10 +41,9 @@ static void mantis_hifevm_work(struct work_struct *work) struct mantis_ca *ca = container_of(work, struct mantis_ca, hif_evm_work); struct mantis_pci *mantis = ca->ca_priv; - u32 gpif_stat, gpif_mask; + u32 gpif_stat; gpif_stat = mmread(MANTIS_GPIF_STATUS); - gpif_mask = mmread(MANTIS_GPIF_IRQCFG); if (gpif_stat & MANTIS_GPIF_DETSTAT) { if (gpif_stat & MANTIS_CARD_PLUGIN) { diff --git a/drivers/media/dvb/ngene/ngene-core.c b/drivers/media/dvb/ngene/ngene-core.c index f129a9303f80..39857384af10 100644 --- a/drivers/media/dvb/ngene/ngene-core.c +++ b/drivers/media/dvb/ngene/ngene-core.c @@ -1409,10 +1409,8 @@ static int ngene_start(struct ngene *dev) if (stat < 0) goto fail; - if (!stat) - return stat; + return 0; - /* otherwise error: fall through */ fail: ngwritel(0, NGENE_INT_ENABLE); free_irq(dev->pci_dev->irq, dev); diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c index e1f20c236989..f148b19a206a 100644 --- a/drivers/media/dvb/pluto2/pluto2.c +++ b/drivers/media/dvb/pluto2/pluto2.c @@ -481,14 +481,6 @@ static int lg_tdtpe001p_tuner_set_params(struct dvb_frontend *fe) if (p->bandwidth_hz == 8000000) buf[3] |= 0x08; - if (sizeof(buf) == 6) { - buf[4] = buf[2]; - buf[4] &= ~0x1c; - buf[4] |= 0x18; - - buf[5] = (0 << 7) | (2 << 4); - } - msg.addr = I2C_ADDR_TUA6034 >> 1; msg.flags = 0; msg.buf = buf; diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c index 91f8c8291e2b..d6f3f100699a 100644 --- a/drivers/media/dvb/siano/smssdio.c +++ b/drivers/media/dvb/siano/smssdio.c @@ -114,7 +114,7 @@ out: static void smssdio_interrupt(struct sdio_func *func) { - int ret, isr; + int ret; struct smssdio_device *smsdev; struct smscore_buffer_t *cb; @@ -127,7 +127,7 @@ static void smssdio_interrupt(struct sdio_func *func) * The interrupt register has no defined meaning. It is just * a way of turning of the level triggered interrupt. */ - isr = sdio_readb(func, SMSSDIO_INT, &ret); + (void)sdio_readb(func, SMSSDIO_INT, &ret); if (ret) { sms_err("Unable to read interrupt register!\n"); return; diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c index b1fe5137df09..63c004a25e0b 100644 --- a/drivers/media/dvb/siano/smsusb.c +++ b/drivers/media/dvb/siano/smsusb.c @@ -542,6 +542,8 @@ static const struct usb_device_id smsusb_id_table[] __devinitconst = { .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { USB_DEVICE(0x2040, 0xc090), .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, + { USB_DEVICE(0x2040, 0xc0a0), + .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM }, { } /* Terminating entry */ }; diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c index ee8ee1d481fa..1b2d15140a1d 100644 --- a/drivers/media/dvb/ttpci/av7110_v4l.c +++ b/drivers/media/dvb/ttpci/av7110_v4l.c @@ -107,7 +107,7 @@ static struct v4l2_input inputs[4] = { .index = 1, .name = "Television", .type = V4L2_INPUT_TYPE_TUNER, - .audioset = 2, + .audioset = 1, .tuner = 0, .std = V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, .status = 0, @@ -494,7 +494,7 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) dprintk(2, "VIDIOC_S_INPUT: %d\n", input); if (!av7110->analog_tuner_flags) - return 0; + return input ? -EINVAL : 0; if (input >= 4) return -EINVAL; @@ -503,19 +503,38 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) return av7110_dvb_c_switch(fh); } -static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) { dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); if (a->index != 0) return -EINVAL; - memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio)); + *a = msp3400_v4l2_audio; + return 0; +} + +static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + + dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index); + if (a->index != 0) + return -EINVAL; + if (av7110->current_input >= 2) + return -EINVAL; + *a = msp3400_v4l2_audio; return 0; } static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) { + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct av7110 *av7110 = (struct av7110 *)dev->ext_priv; + dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index); - return 0; + if (av7110->current_input >= 2) + return -EINVAL; + return a->index ? -EINVAL : 0; } static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh, @@ -802,26 +821,39 @@ int av7110_init_v4l(struct av7110 *av7110) ERR("cannot init capture device. skipping\n"); return -ENODEV; } - vv_data->ops.vidioc_enum_input = vidioc_enum_input; - vv_data->ops.vidioc_g_input = vidioc_g_input; - vv_data->ops.vidioc_s_input = vidioc_s_input; - vv_data->ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data->ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data->ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data->ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data->ops.vidioc_g_audio = vidioc_g_audio; - vv_data->ops.vidioc_s_audio = vidioc_s_audio; - vv_data->ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; - vv_data->ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; - vv_data->ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; + vv_data->vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data->vid_ops.vidioc_g_input = vidioc_g_input; + vv_data->vid_ops.vidioc_s_input = vidioc_s_input; + vv_data->vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data->vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data->vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data->vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vid_ops.vidioc_enumaudio = vidioc_enumaudio; + vv_data->vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data->vid_ops.vidioc_s_audio = vidioc_s_audio; + vv_data->vid_ops.vidioc_g_fmt_vbi_cap = NULL; + + vv_data->vbi_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data->vbi_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data->vbi_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data->vbi_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data->vbi_ops.vidioc_g_fmt_vbi_cap = NULL; + vv_data->vbi_ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap; + vv_data->vbi_ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out; + vv_data->vbi_ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out; + + if (FW_VERSION(av7110->arm_app) < 0x2623) + vv_data->capabilities &= ~V4L2_CAP_SLICED_VBI_OUTPUT; if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) { ERR("cannot register capture device. skipping\n"); saa7146_vv_release(dev); return -ENODEV; } - if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) - ERR("cannot register vbi v4l2 device. skipping\n"); + if (FW_VERSION(av7110->arm_app) >= 0x2623) { + if (saa7146_register_device(&av7110->vbi_dev, dev, "av7110", VFL_TYPE_VBI)) + ERR("cannot register vbi v4l2 device. skipping\n"); + } return 0; } @@ -905,7 +937,7 @@ static int std_callback(struct saa7146_dev* dev, struct saa7146_standard *std) static struct saa7146_ext_vv av7110_vv_data_st = { .inputs = 1, .audios = 1, - .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT, + .capabilities = V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, .flags = 0, .stds = &standard[0], @@ -920,7 +952,7 @@ static struct saa7146_ext_vv av7110_vv_data_st = { static struct saa7146_ext_vv av7110_vv_data_c = { .inputs = 1, .audios = 1, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT | V4L2_CAP_AUDIO, .flags = SAA7146_USE_PORT_B_FOR_VBI, .stds = &standard[0], diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c index 8b32e282bf5d..12ddb53c58dc 100644 --- a/drivers/media/dvb/ttpci/budget-av.c +++ b/drivers/media/dvb/ttpci/budget-av.c @@ -1483,9 +1483,9 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio ERR("cannot init vv subsystem\n"); return err; } - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) { /* fixme: proper cleanup here */ diff --git a/drivers/media/media-entity.c b/drivers/media/media-entity.c index 056138f63c7d..e1cd13283407 100644 --- a/drivers/media/media-entity.c +++ b/drivers/media/media-entity.c @@ -214,23 +214,76 @@ EXPORT_SYMBOL_GPL(media_entity_graph_walk_next); * pipeline pointer must be identical for all nested calls to * media_entity_pipeline_start(). */ -void media_entity_pipeline_start(struct media_entity *entity, - struct media_pipeline *pipe) +__must_check int media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe) { struct media_device *mdev = entity->parent; struct media_entity_graph graph; + struct media_entity *entity_err = entity; + int ret; mutex_lock(&mdev->graph_mutex); media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { + unsigned int i; + entity->stream_count++; WARN_ON(entity->pipe && entity->pipe != pipe); entity->pipe = pipe; + + /* Already streaming --- no need to check. */ + if (entity->stream_count > 1) + continue; + + if (!entity->ops || !entity->ops->link_validate) + continue; + + for (i = 0; i < entity->num_links; i++) { + struct media_link *link = &entity->links[i]; + + /* Is this pad part of an enabled link? */ + if (!(link->flags & MEDIA_LNK_FL_ENABLED)) + continue; + + /* Are we the sink or not? */ + if (link->sink->entity != entity) + continue; + + ret = entity->ops->link_validate(link); + if (ret < 0 && ret != -ENOIOCTLCMD) + goto error; + } } mutex_unlock(&mdev->graph_mutex); + + return 0; + +error: + /* + * Link validation on graph failed. We revert what we did and + * return the error. + */ + media_entity_graph_walk_start(&graph, entity_err); + + while ((entity_err = media_entity_graph_walk_next(&graph))) { + entity_err->stream_count--; + if (entity_err->stream_count == 0) + entity_err->pipe = NULL; + + /* + * We haven't increased stream_count further than this + * so we quit here. + */ + if (entity_err == entity) + break; + } + + mutex_unlock(&mdev->graph_mutex); + + return ret; } EXPORT_SYMBOL_GPL(media_entity_pipeline_start); diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig index 8db2d7f4b52a..c257da13d766 100644 --- a/drivers/media/radio/Kconfig +++ b/drivers/media/radio/Kconfig @@ -320,7 +320,7 @@ config RADIO_MIROPCM20 module will be called radio-miropcm20. config RADIO_SF16FMI - tristate "SF16-FMI/SF16-FMP Radio" + tristate "SF16-FMI/SF16-FMP/SF16-FMD Radio" depends on ISA && VIDEO_V4L2 ---help--- Choose Y here if you have one of these FM radio cards. @@ -329,7 +329,7 @@ config RADIO_SF16FMI module will be called radio-sf16fmi. config RADIO_SF16FMR2 - tristate "SF16FMR2 Radio" + tristate "SF16-FMR2/SF16-FMD2 Radio" depends on ISA && VIDEO_V4L2 && SND ---help--- Choose Y here if you have one of these FM radio cards. diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c index f36905b63645..63b112b555b2 100644 --- a/drivers/media/radio/dsbr100.c +++ b/drivers/media/radio/dsbr100.c @@ -1,92 +1,37 @@ /* A driver for the D-Link DSB-R100 USB radio and Gemtek USB Radio 21. - The device plugs into both the USB and an analog audio input, so this thing - only deals with initialisation and frequency setting, the - audio data has to be handled by a sound driver. - - Major issue: I can't find out where the device reports the signal - strength, and indeed the windows software appearantly just looks - at the stereo indicator as well. So, scanning will only find - stereo stations. Sad, but I can't help it. - - Also, the windows program sends oodles of messages over to the - device, and I couldn't figure out their meaning. My suspicion - is that they don't have any:-) - - You might find some interesting stuff about this module at - http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr - - Copyright (c) 2000 Markus Demleitner - - 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 - - History: - - Version 0.46: - Removed usb_dsbr100_open/close calls and radio->users counter. Also, - radio->muted changed to radio->status and suspend/resume calls updated. - - Version 0.45: - Converted to v4l2_device. - - Version 0.44: - Add suspend/resume functions, fix unplug of device, - a lot of cleanups and fixes by Alexey Klimov - - Version 0.43: - Oliver Neukum: avoided DMA coherency issue - - Version 0.42: - Converted dsbr100 to use video_ioctl2 - by Douglas Landgraf - - Version 0.41-ac1: - Alan Cox: Some cleanups and fixes - - Version 0.41: - Converted to V4L2 API by Mauro Carvalho Chehab - - Version 0.40: - Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing - - Version 0.30: - Markus: Updates for 2.5.x kernel and more ISO compliant source - - Version 0.25: - PSL and Markus: Cleanup, radio now doesn't stop on device close - - Version 0.24: - Markus: Hope I got these silly VIDEO_TUNER_LOW issues finally - right. Some minor cleanup, improved standalone compilation - - Version 0.23: - Markus: Sign extension bug fixed by declaring transfer_buffer unsigned - - Version 0.22: - Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, - thanks to Mike Cox for pointing the problem out. - - Version 0.21: - Markus: Minor cleanup, warnings if something goes wrong, lame attempt - to adhere to Documentation/CodingStyle - - Version 0.2: - Brad Hards : Fixes to make it work as non-module - Markus: Copyright clarification - - Version 0.01: Markus: initial release - + * The device plugs into both the USB and an analog audio input, so this thing + * only deals with initialisation and frequency setting, the + * audio data has to be handled by a sound driver. + * + * Major issue: I can't find out where the device reports the signal + * strength, and indeed the windows software appearantly just looks + * at the stereo indicator as well. So, scanning will only find + * stereo stations. Sad, but I can't help it. + * + * Also, the windows program sends oodles of messages over to the + * device, and I couldn't figure out their meaning. My suspicion + * is that they don't have any:-) + * + * You might find some interesting stuff about this module at + * http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr + * + * Fully tested with the Keene USB FM Transmitter and the v4l2-compliance tool. + * + * Copyright (c) 2000 Markus Demleitner + * + * 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 */ #include @@ -95,17 +40,19 @@ #include #include #include +#include #include #include -#include +#include +#include /* * Version Information */ -#define DRIVER_VERSION "0.4.7" - -#define DRIVER_AUTHOR "Markus Demleitner " -#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver" +MODULE_AUTHOR("Markus Demleitner "); +MODULE_DESCRIPTION("D-Link DSB-R100 USB FM radio driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION("1.1.0"); #define DSB100_VENDOR 0x04b4 #define DSB100_PRODUCT 0x1002 @@ -122,19 +69,8 @@ devices, that would be 76 and 91. */ #define FREQ_MAX 108.0 #define FREQ_MUL 16000 -/* defines for radio->status */ -#define STARTED 0 -#define STOPPED 1 - #define v4l2_dev_to_radio(d) container_of(d, struct dsbr100_device, v4l2_dev) -static int usb_dsbr100_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void usb_dsbr100_disconnect(struct usb_interface *intf); -static int usb_dsbr100_suspend(struct usb_interface *intf, - pm_message_t message); -static int usb_dsbr100_resume(struct usb_interface *intf); - static int radio_nr = -1; module_param(radio_nr, int, 0); @@ -143,70 +79,58 @@ struct dsbr100_device { struct usb_device *usbdev; struct video_device videodev; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; u8 *transfer_buffer; struct mutex v4l2_lock; int curfreq; - int stereo; - int status; -}; - -static struct usb_device_id usb_dsbr100_device_table [] = { - { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE (usb, usb_dsbr100_device_table); - -/* USB subsystem interface */ -static struct usb_driver usb_dsbr100_driver = { - .name = "dsbr100", - .probe = usb_dsbr100_probe, - .disconnect = usb_dsbr100_disconnect, - .id_table = usb_dsbr100_device_table, - .suspend = usb_dsbr100_suspend, - .resume = usb_dsbr100_resume, - .reset_resume = usb_dsbr100_resume, - .supports_autosuspend = 0, + bool stereo; + bool muted; }; /* Low-level device interface begins here */ +/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ +static int dsbr100_setfreq(struct dsbr100_device *radio, unsigned freq) +{ + unsigned f = (freq / 16 * 80) / 1000 + 856; + int retval = 0; + + if (!radio->muted) { + retval = usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_TUNE, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + (f >> 8) & 0x00ff, f & 0xff, + radio->transfer_buffer, 8, 300); + if (retval >= 0) + mdelay(1); + } + + if (retval >= 0) { + radio->curfreq = freq; + return 0; + } + dev_err(&radio->usbdev->dev, + "%s - usb_control_msg returned %i, request %i\n", + __func__, retval, DSB100_TUNE); + return retval; +} + /* switch on radio */ static int dsbr100_start(struct dsbr100_device *radio) { - int retval; - int request; - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00, 0xC7, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = USB_REQ_GET_STATUS; - goto usb_control_msg_failed; - } - - retval = usb_control_msg(radio->usbdev, + int retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), DSB100_ONOFF, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 0x01, 0x00, radio->transfer_buffer, 8, 300); - if (retval < 0) { - request = DSB100_ONOFF; - goto usb_control_msg_failed; - } - - radio->status = STARTED; - return (radio->transfer_buffer)[0]; - -usb_control_msg_failed: + if (retval >= 0) + return dsbr100_setfreq(radio, radio->curfreq); dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", - __func__, retval, request); + __func__, retval, DSB100_ONOFF); return retval; } @@ -214,108 +138,33 @@ usb_control_msg_failed: /* switch off radio */ static int dsbr100_stop(struct dsbr100_device *radio) { - int retval; - int request; - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x16, 0x1C, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = USB_REQ_GET_STATUS; - goto usb_control_msg_failed; - } - - retval = usb_control_msg(radio->usbdev, + int retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), DSB100_ONOFF, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, 0x00, 0x00, radio->transfer_buffer, 8, 300); - if (retval < 0) { - request = DSB100_ONOFF; - goto usb_control_msg_failed; - } - - radio->status = STOPPED; - return (radio->transfer_buffer)[0]; - -usb_control_msg_failed: + if (retval >= 0) + return 0; dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", - __func__, retval, request); + __func__, retval, DSB100_ONOFF); return retval; } -/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ -static int dsbr100_setfreq(struct dsbr100_device *radio) -{ - int retval; - int request; - int freq = (radio->curfreq / 16 * 80) / 1000 + 856; - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - DSB100_TUNE, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - (freq >> 8) & 0x00ff, freq & 0xff, - radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = DSB100_TUNE; - goto usb_control_msg_failed; - } - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x96, 0xB7, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = USB_REQ_GET_STATUS; - goto usb_control_msg_failed; - } - - retval = usb_control_msg(radio->usbdev, - usb_rcvctrlpipe(radio->usbdev, 0), - USB_REQ_GET_STATUS, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00, 0x24, radio->transfer_buffer, 8, 300); - - if (retval < 0) { - request = USB_REQ_GET_STATUS; - goto usb_control_msg_failed; - } - - radio->stereo = !((radio->transfer_buffer)[0] & 0x01); - return (radio->transfer_buffer)[0]; - -usb_control_msg_failed: - radio->stereo = -1; - dev_err(&radio->usbdev->dev, - "%s - usb_control_msg returned %i, request %i\n", - __func__, retval, request); - return retval; -} - /* return the device status. This is, in effect, just whether it sees a stereo signal or not. Pity. */ static void dsbr100_getstat(struct dsbr100_device *radio) { - int retval; - - retval = usb_control_msg(radio->usbdev, + int retval = usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0), USB_REQ_GET_STATUS, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0x00 , 0x24, radio->transfer_buffer, 8, 300); + 0x00, 0x24, radio->transfer_buffer, 8, 300); if (retval < 0) { - radio->stereo = -1; + radio->stereo = false; dev_err(&radio->usbdev->dev, "%s - usb_control_msg returned %i, request %i\n", __func__, retval, USB_REQ_GET_STATUS); @@ -332,7 +181,8 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(v->driver, "dsbr100", sizeof(v->driver)); strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER; + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -349,13 +199,11 @@ static int vidioc_g_tuner(struct file *file, void *priv, v->type = V4L2_TUNER_RADIO; v->rangelow = FREQ_MIN * FREQ_MUL; v->rangehigh = FREQ_MAX * FREQ_MUL; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if(radio->stereo) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - v->audmode = V4L2_TUNER_MODE_MONO; - v->signal = 0xffff; /* We can't get the signal strength */ + v->rxsubchans = radio->stereo ? V4L2_TUNER_SUB_STEREO : + V4L2_TUNER_SUB_MONO; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + v->audmode = V4L2_TUNER_MODE_STEREO; + v->signal = radio->stereo ? 0xffff : 0; /* We can't get the signal strength */ return 0; } @@ -369,14 +217,12 @@ static int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) { struct dsbr100_device *radio = video_drvdata(file); - int retval; - radio->curfreq = f->frequency; + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + return -EINVAL; - retval = dsbr100_setfreq(radio); - if (retval < 0) - dev_warn(&radio->usbdev->dev, "Set frequency failed\n"); - return 0; + return dsbr100_setfreq(radio, clamp_t(unsigned, f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); } static int vidioc_g_frequency(struct file *file, void *priv, @@ -384,90 +230,26 @@ static int vidioc_g_frequency(struct file *file, void *priv, { struct dsbr100_device *radio = video_drvdata(file); + if (f->tuner) + return -EINVAL; f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; return 0; } -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int usb_dsbr100_s_ctrl(struct v4l2_ctrl *ctrl) { - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - - return -EINVAL; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct dsbr100_device *radio = video_drvdata(file); + struct dsbr100_device *radio = + container_of(ctrl->handler, struct dsbr100_device, hdl); switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: - ctrl->value = radio->status; - return 0; + radio->muted = ctrl->val; + return radio->muted ? dsbr100_stop(radio) : dsbr100_start(radio); } return -EINVAL; } -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct dsbr100_device *radio = video_drvdata(file); - int retval; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) { - retval = dsbr100_stop(radio); - if (retval < 0) { - dev_warn(&radio->usbdev->dev, - "Radio did not respond properly\n"); - return -EBUSY; - } - } else { - retval = dsbr100_start(radio); - if (retval < 0) { - dev_warn(&radio->usbdev->dev, - "Radio did not respond properly\n"); - return -EBUSY; - } - } - return 0; - } - return -EINVAL; -} - -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - if (a->index > 1) - return -EINVAL; - - strcpy(a->name, "Radio"); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} - -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - return i ? -EINVAL : 0; -} - -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - return a->index ? -EINVAL : 0; -} /* USB subsystem interface begins here */ @@ -481,8 +263,17 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) { struct dsbr100_device *radio = usb_get_intfdata(intf); - v4l2_device_get(&radio->v4l2_dev); mutex_lock(&radio->v4l2_lock); + /* + * Disconnect is also called on unload, and in that case we need to + * mute the device. This call will silently fail if it is called + * after a physical disconnect. + */ + usb_control_msg(radio->usbdev, + usb_rcvctrlpipe(radio->usbdev, 0), + DSB100_ONOFF, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0x00, 0x00, radio->transfer_buffer, 8, 300); usb_set_intfdata(intf, NULL); video_unregister_device(&radio->videodev); v4l2_device_disconnect(&radio->v4l2_dev); @@ -495,25 +286,13 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf) static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) { struct dsbr100_device *radio = usb_get_intfdata(intf); - int retval; mutex_lock(&radio->v4l2_lock); - if (radio->status == STARTED) { - retval = dsbr100_stop(radio); - if (retval < 0) - dev_warn(&intf->dev, "dsbr100_stop failed\n"); - - /* After dsbr100_stop() status set to STOPPED. - * If we want driver to start radio on resume - * we set status equal to STARTED. - * On resume we will check status and run radio if needed. - */ - radio->status = STARTED; - } + if (!radio->muted && dsbr100_stop(radio) < 0) + dev_warn(&intf->dev, "dsbr100_stop failed\n"); mutex_unlock(&radio->v4l2_lock); dev_info(&intf->dev, "going into suspend..\n"); - return 0; } @@ -521,18 +300,13 @@ static int usb_dsbr100_suspend(struct usb_interface *intf, pm_message_t message) static int usb_dsbr100_resume(struct usb_interface *intf) { struct dsbr100_device *radio = usb_get_intfdata(intf); - int retval; mutex_lock(&radio->v4l2_lock); - if (radio->status == STARTED) { - retval = dsbr100_start(radio); - if (retval < 0) - dev_warn(&intf->dev, "dsbr100_start failed\n"); - } + if (!radio->muted && dsbr100_start(radio) < 0) + dev_warn(&intf->dev, "dsbr100_start failed\n"); mutex_unlock(&radio->v4l2_lock); dev_info(&intf->dev, "coming out of suspend..\n"); - return 0; } @@ -541,15 +315,23 @@ static void usb_dsbr100_release(struct v4l2_device *v4l2_dev) { struct dsbr100_device *radio = v4l2_dev_to_radio(v4l2_dev); + v4l2_ctrl_handler_free(&radio->hdl); v4l2_device_unregister(&radio->v4l2_dev); kfree(radio->transfer_buffer); kfree(radio); } +static const struct v4l2_ctrl_ops usb_dsbr100_ctrl_ops = { + .s_ctrl = usb_dsbr100_s_ctrl, +}; + /* File system interface */ static const struct v4l2_file_operations usb_dsbr100_fops = { .owner = THIS_MODULE, .unlocked_ioctl = video_ioctl2, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, }; static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { @@ -558,13 +340,9 @@ static const struct v4l2_ioctl_ops usb_dsbr100_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* check if the device is present and register with v4l and usb if it is */ @@ -593,11 +371,17 @@ static int usb_dsbr100_probe(struct usb_interface *intf, retval = v4l2_device_register(&intf->dev, v4l2_dev); if (retval < 0) { v4l2_err(v4l2_dev, "couldn't register v4l2_device\n"); - kfree(radio->transfer_buffer); - kfree(radio); - return retval; + goto err_reg_dev; } + v4l2_ctrl_handler_init(&radio->hdl, 1); + v4l2_ctrl_new_std(&radio->hdl, &usb_dsbr100_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (radio->hdl.error) { + retval = radio->hdl.error; + v4l2_err(v4l2_dev, "couldn't register control\n"); + goto err_reg_ctrl; + } mutex_init(&radio->v4l2_lock); strlcpy(radio->videodev.name, v4l2_dev->name, sizeof(radio->videodev.name)); radio->videodev.v4l2_dev = v4l2_dev; @@ -605,28 +389,46 @@ static int usb_dsbr100_probe(struct usb_interface *intf, radio->videodev.ioctl_ops = &usb_dsbr100_ioctl_ops; radio->videodev.release = video_device_release_empty; radio->videodev.lock = &radio->v4l2_lock; + radio->videodev.ctrl_handler = &radio->hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); radio->usbdev = interface_to_usbdev(intf); radio->curfreq = FREQ_MIN * FREQ_MUL; - radio->status = STOPPED; + radio->muted = true; video_set_drvdata(&radio->videodev, radio); + usb_set_intfdata(intf, radio); retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); - if (retval < 0) { - v4l2_err(v4l2_dev, "couldn't register video device\n"); - v4l2_device_unregister(v4l2_dev); - kfree(radio->transfer_buffer); - kfree(radio); - return -EIO; - } - usb_set_intfdata(intf, radio); - return 0; + if (retval == 0) + return 0; + v4l2_err(v4l2_dev, "couldn't register video device\n"); + +err_reg_ctrl: + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(v4l2_dev); +err_reg_dev: + kfree(radio->transfer_buffer); + kfree(radio); + return retval; } -module_usb_driver(usb_dsbr100_driver); +static struct usb_device_id usb_dsbr100_device_table[] = { + { USB_DEVICE(DSB100_VENDOR, DSB100_PRODUCT) }, + { } /* Terminating entry */ +}; -MODULE_AUTHOR( DRIVER_AUTHOR ); -MODULE_DESCRIPTION( DRIVER_DESC ); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); +MODULE_DEVICE_TABLE(usb, usb_dsbr100_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_dsbr100_driver = { + .name = "dsbr100", + .probe = usb_dsbr100_probe, + .disconnect = usb_dsbr100_disconnect, + .id_table = usb_dsbr100_device_table, + .suspend = usb_dsbr100_suspend, + .resume = usb_dsbr100_resume, + .reset_resume = usb_dsbr100_resume, +}; + +module_usb_driver(usb_dsbr100_driver); diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c index 2e639ce6f256..235c0e349820 100644 --- a/drivers/media/radio/radio-gemtek.c +++ b/drivers/media/radio/radio-gemtek.c @@ -29,6 +29,7 @@ #include /* kernel radio structs */ #include #include /* outb, outb_p */ +#include #include #include #include @@ -283,6 +284,16 @@ static const struct radio_isa_ops gemtek_ops = { static const int gemtek_ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c }; +#ifdef CONFIG_PNP +static struct pnp_device_id gemtek_pnp_devices[] = { + /* AOpen FX-3D/Pro Radio */ + {.id = "ADS7183", .driver_data = 0}, + {.id = ""} +}; + +MODULE_DEVICE_TABLE(pnp, gemtek_pnp_devices); +#endif + static struct radio_isa_driver gemtek_driver = { .driver = { .match = radio_isa_match, @@ -292,6 +303,14 @@ static struct radio_isa_driver gemtek_driver = { .name = "radio-gemtek", }, }, +#ifdef CONFIG_PNP + .pnp_driver = { + .name = "radio-gemtek", + .id_table = gemtek_pnp_devices, + .probe = radio_isa_pnp_probe, + .remove = radio_isa_pnp_remove, + }, +#endif .io_params = io, .radio_nr_params = radio_nr, .io_ports = gemtek_ioports, @@ -305,12 +324,18 @@ static struct radio_isa_driver gemtek_driver = { static int __init gemtek_init(void) { gemtek_driver.probe = probe; +#ifdef CONFIG_PNP + pnp_register_driver(&gemtek_driver.pnp_driver); +#endif return isa_register_driver(&gemtek_driver.driver, GEMTEK_MAX); } static void __exit gemtek_exit(void) { hardmute = 1; /* Turn off PLL */ +#ifdef CONFIG_PNP + pnp_unregister_driver(&gemtek_driver.pnp_driver); +#endif isa_unregister_driver(&gemtek_driver.driver); } diff --git a/drivers/media/radio/radio-isa.c b/drivers/media/radio/radio-isa.c index 06f906351fad..3c0067de4324 100644 --- a/drivers/media/radio/radio-isa.c +++ b/drivers/media/radio/radio-isa.c @@ -150,14 +150,6 @@ static int radio_isa_log_status(struct file *file, void *priv) return 0; } -static int radio_isa_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - if (sub->type == V4L2_EVENT_CTRL) - return v4l2_event_subscribe(fh, sub, 0); - return -EINVAL; -} - static const struct v4l2_ctrl_ops radio_isa_ctrl_ops = { .s_ctrl = radio_isa_s_ctrl, }; @@ -177,7 +169,7 @@ static const struct v4l2_ioctl_ops radio_isa_ioctl_ops = { .vidioc_g_frequency = radio_isa_g_frequency, .vidioc_s_frequency = radio_isa_s_frequency, .vidioc_log_status = radio_isa_log_status, - .vidioc_subscribe_event = radio_isa_subscribe_event, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -199,56 +191,31 @@ static bool radio_isa_valid_io(const struct radio_isa_driver *drv, int io) return false; } -int radio_isa_probe(struct device *pdev, unsigned int dev) +struct radio_isa_card *radio_isa_alloc(struct radio_isa_driver *drv, + struct device *pdev) { - struct radio_isa_driver *drv = pdev->platform_data; - const struct radio_isa_ops *ops = drv->ops; struct v4l2_device *v4l2_dev; - struct radio_isa_card *isa; - int res; + struct radio_isa_card *isa = drv->ops->alloc(); + if (!isa) + return NULL; - isa = drv->ops->alloc(); - if (isa == NULL) - return -ENOMEM; dev_set_drvdata(pdev, isa); isa->drv = drv; - isa->io = drv->io_params[dev]; v4l2_dev = &isa->v4l2_dev; strlcpy(v4l2_dev->name, dev_name(pdev), sizeof(v4l2_dev->name)); - if (drv->probe && ops->probe) { - int i; + return isa; +} - for (i = 0; i < drv->num_of_io_ports; ++i) { - int io = drv->io_ports[i]; +int radio_isa_common_probe(struct radio_isa_card *isa, struct device *pdev, + int radio_nr, unsigned region_size) +{ + const struct radio_isa_driver *drv = isa->drv; + const struct radio_isa_ops *ops = drv->ops; + struct v4l2_device *v4l2_dev = &isa->v4l2_dev; + int res; - if (request_region(io, drv->region_size, v4l2_dev->name)) { - bool found = ops->probe(isa, io); - - release_region(io, drv->region_size); - if (found) { - isa->io = io; - break; - } - } - } - } - - if (!radio_isa_valid_io(drv, isa->io)) { - int i; - - if (isa->io < 0) - return -ENODEV; - v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x", - drv->io_ports[0]); - for (i = 1; i < drv->num_of_io_ports; i++) - printk(KERN_CONT "/0x%03x", drv->io_ports[i]); - printk(KERN_CONT ".\n"); - kfree(isa); - return -EINVAL; - } - - if (!request_region(isa->io, drv->region_size, v4l2_dev->name)) { + if (!request_region(isa->io, region_size, v4l2_dev->name)) { v4l2_err(v4l2_dev, "port 0x%x already in use\n", isa->io); kfree(isa); return -EBUSY; @@ -299,42 +266,126 @@ int radio_isa_probe(struct device *pdev, unsigned int dev) res = ops->s_stereo(isa, isa->stereo); if (res < 0) { v4l2_err(v4l2_dev, "Could not setup card\n"); - goto err_node_reg; + goto err_hdl; } - res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, - drv->radio_nr_params[dev]); + res = video_register_device(&isa->vdev, VFL_TYPE_RADIO, radio_nr); + if (res < 0) { v4l2_err(v4l2_dev, "Could not register device node\n"); - goto err_node_reg; + goto err_hdl; } v4l2_info(v4l2_dev, "Initialized radio card %s on port 0x%03x\n", drv->card, isa->io); return 0; -err_node_reg: - v4l2_ctrl_handler_free(&isa->hdl); err_hdl: - v4l2_device_unregister(&isa->v4l2_dev); + v4l2_ctrl_handler_free(&isa->hdl); err_dev_reg: - release_region(isa->io, drv->region_size); + release_region(isa->io, region_size); kfree(isa); return res; } -EXPORT_SYMBOL_GPL(radio_isa_probe); -int radio_isa_remove(struct device *pdev, unsigned int dev) +int radio_isa_common_remove(struct radio_isa_card *isa, unsigned region_size) { - struct radio_isa_card *isa = dev_get_drvdata(pdev); const struct radio_isa_ops *ops = isa->drv->ops; ops->s_mute_volume(isa, true, isa->volume ? isa->volume->cur.val : 0); video_unregister_device(&isa->vdev); v4l2_ctrl_handler_free(&isa->hdl); v4l2_device_unregister(&isa->v4l2_dev); - release_region(isa->io, isa->drv->region_size); + release_region(isa->io, region_size); v4l2_info(&isa->v4l2_dev, "Removed radio card %s\n", isa->drv->card); kfree(isa); return 0; } + +int radio_isa_probe(struct device *pdev, unsigned int dev) +{ + struct radio_isa_driver *drv = pdev->platform_data; + const struct radio_isa_ops *ops = drv->ops; + struct v4l2_device *v4l2_dev; + struct radio_isa_card *isa; + + isa = radio_isa_alloc(drv, pdev); + if (!isa) + return -ENOMEM; + isa->io = drv->io_params[dev]; + v4l2_dev = &isa->v4l2_dev; + + if (drv->probe && ops->probe) { + int i; + + for (i = 0; i < drv->num_of_io_ports; ++i) { + int io = drv->io_ports[i]; + + if (request_region(io, drv->region_size, v4l2_dev->name)) { + bool found = ops->probe(isa, io); + + release_region(io, drv->region_size); + if (found) { + isa->io = io; + break; + } + } + } + } + + if (!radio_isa_valid_io(drv, isa->io)) { + int i; + + if (isa->io < 0) + return -ENODEV; + v4l2_err(v4l2_dev, "you must set an I/O address with io=0x%03x", + drv->io_ports[0]); + for (i = 1; i < drv->num_of_io_ports; i++) + printk(KERN_CONT "/0x%03x", drv->io_ports[i]); + printk(KERN_CONT ".\n"); + kfree(isa); + return -EINVAL; + } + + return radio_isa_common_probe(isa, pdev, drv->radio_nr_params[dev], + drv->region_size); +} +EXPORT_SYMBOL_GPL(radio_isa_probe); + +int radio_isa_remove(struct device *pdev, unsigned int dev) +{ + struct radio_isa_card *isa = dev_get_drvdata(pdev); + + return radio_isa_common_remove(isa, isa->drv->region_size); +} EXPORT_SYMBOL_GPL(radio_isa_remove); + +#ifdef CONFIG_PNP +int radio_isa_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id) +{ + struct pnp_driver *pnp_drv = to_pnp_driver(dev->dev.driver); + struct radio_isa_driver *drv = container_of(pnp_drv, + struct radio_isa_driver, pnp_driver); + struct radio_isa_card *isa; + + if (!pnp_port_valid(dev, 0)) + return -ENODEV; + + isa = radio_isa_alloc(drv, &dev->dev); + if (!isa) + return -ENOMEM; + + isa->io = pnp_port_start(dev, 0); + + return radio_isa_common_probe(isa, &dev->dev, drv->radio_nr_params[0], + pnp_port_len(dev, 0)); +} +EXPORT_SYMBOL_GPL(radio_isa_pnp_probe); + +void radio_isa_pnp_remove(struct pnp_dev *dev) +{ + struct radio_isa_card *isa = dev_get_drvdata(&dev->dev); + + radio_isa_common_remove(isa, pnp_port_len(dev, 0)); +} +EXPORT_SYMBOL_GPL(radio_isa_pnp_remove); +#endif diff --git a/drivers/media/radio/radio-isa.h b/drivers/media/radio/radio-isa.h index 8a0ea84d86de..ba4c01f1bd0c 100644 --- a/drivers/media/radio/radio-isa.h +++ b/drivers/media/radio/radio-isa.h @@ -24,6 +24,7 @@ #define _RADIO_ISA_H_ #include +#include #include #include #include @@ -76,6 +77,9 @@ struct radio_isa_ops { /* Top level structure needed to instantiate the cards */ struct radio_isa_driver { struct isa_driver driver; +#ifdef CONFIG_PNP + struct pnp_driver pnp_driver; +#endif const struct radio_isa_ops *ops; /* The module_param_array with the specified I/O ports */ int *io_params; @@ -101,5 +105,10 @@ struct radio_isa_driver { int radio_isa_match(struct device *pdev, unsigned int dev); int radio_isa_probe(struct device *pdev, unsigned int dev); int radio_isa_remove(struct device *pdev, unsigned int dev); +#ifdef CONFIG_PNP +int radio_isa_pnp_probe(struct pnp_dev *dev, + const struct pnp_device_id *dev_id); +void radio_isa_pnp_remove(struct pnp_dev *dev); +#endif #endif diff --git a/drivers/media/radio/radio-keene.c b/drivers/media/radio/radio-keene.c index 55bd1d2937c8..79adf3e654e5 100644 --- a/drivers/media/radio/radio-keene.c +++ b/drivers/media/radio/radio-keene.c @@ -28,7 +28,6 @@ #include #include #include -#include #include /* driver and module definitions */ @@ -149,7 +148,6 @@ static void usb_keene_disconnect(struct usb_interface *intf) { struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf)); - v4l2_device_get(&radio->v4l2_dev); mutex_lock(&radio->lock); usb_set_intfdata(intf, NULL); video_unregister_device(&radio->vdev); @@ -158,6 +156,23 @@ static void usb_keene_disconnect(struct usb_interface *intf) v4l2_device_put(&radio->v4l2_dev); } +static int usb_keene_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf)); + + return keene_cmd_main(radio, 0, false); +} + +static int usb_keene_resume(struct usb_interface *intf) +{ + struct keene_device *radio = to_keene_dev(usb_get_intfdata(intf)); + + mdelay(50); + keene_cmd_set(radio); + keene_cmd_main(radio, radio->curfreq, true); + return 0; +} + static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { @@ -256,18 +271,6 @@ static int keene_s_ctrl(struct v4l2_ctrl *ctrl) return -EINVAL; } -static int vidioc_subscribe_event(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub) -{ - switch (sub->type) { - case V4L2_EVENT_CTRL: - return v4l2_event_subscribe(fh, sub, 0); - default: - return -EINVAL; - } -} - - /* File system interface */ static const struct v4l2_file_operations usb_keene_fops = { .owner = THIS_MODULE, @@ -288,7 +291,7 @@ static const struct v4l2_ioctl_ops usb_keene_ioctl_ops = { .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = vidioc_subscribe_event, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -404,6 +407,9 @@ static struct usb_driver usb_keene_driver = { .probe = usb_keene_probe, .disconnect = usb_keene_disconnect, .id_table = usb_keene_device_table, + .suspend = usb_keene_suspend, + .resume = usb_keene_resume, + .reset_resume = usb_keene_resume, }; static int __init keene_init(void) diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c index a860a72a58ec..94cb6bc690f5 100644 --- a/drivers/media/radio/radio-mr800.c +++ b/drivers/media/radio/radio-mr800.c @@ -62,6 +62,8 @@ #include #include #include +#include +#include #include #include @@ -101,12 +103,17 @@ devices, that would be 76 and 91. */ * List isn't full and will be updated with implementation of new functions */ #define AMRADIO_SET_FREQ 0xa4 +#define AMRADIO_GET_READY_FLAG 0xa5 +#define AMRADIO_GET_SIGNAL 0xa7 +#define AMRADIO_GET_FREQ 0xa8 +#define AMRADIO_SET_SEARCH_UP 0xa9 +#define AMRADIO_SET_SEARCH_DOWN 0xaa #define AMRADIO_SET_MUTE 0xab +#define AMRADIO_SET_RIGHT_MUTE 0xac +#define AMRADIO_SET_LEFT_MUTE 0xad #define AMRADIO_SET_MONO 0xae - -/* Comfortable defines for amradio_set_mute */ -#define AMRADIO_START 0x00 -#define AMRADIO_STOP 0x01 +#define AMRADIO_SET_SEARCH_LVL 0xb0 +#define AMRADIO_STOP_SEARCH 0xb1 /* Comfortable defines for amradio_set_stereo */ #define WANT_STEREO 0x00 @@ -117,29 +124,20 @@ static int radio_nr = -1; module_param(radio_nr, int, 0); MODULE_PARM_DESC(radio_nr, "Radio Nr"); -static int usb_amradio_probe(struct usb_interface *intf, - const struct usb_device_id *id); -static void usb_amradio_disconnect(struct usb_interface *intf); -static int usb_amradio_open(struct file *file); -static int usb_amradio_close(struct file *file); -static int usb_amradio_suspend(struct usb_interface *intf, - pm_message_t message); -static int usb_amradio_resume(struct usb_interface *intf); - /* Data for one (physical) device */ struct amradio_device { /* reference to USB and video device */ struct usb_device *usbdev; struct usb_interface *intf; - struct video_device videodev; + struct video_device vdev; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; - unsigned char *buffer; + u8 *buffer; struct mutex lock; /* buffer locking */ int curfreq; int stereo; int muted; - int initialized; }; static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev) @@ -147,29 +145,8 @@ static inline struct amradio_device *to_amradio_dev(struct v4l2_device *v4l2_dev return container_of(v4l2_dev, struct amradio_device, v4l2_dev); } -/* USB Device ID List */ -static struct usb_device_id usb_amradio_device_table[] = { - {USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT, - USB_CLASS_HID, 0, 0) }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, usb_amradio_device_table); - -/* USB subsystem interface */ -static struct usb_driver usb_amradio_driver = { - .name = MR800_DRIVER_NAME, - .probe = usb_amradio_probe, - .disconnect = usb_amradio_disconnect, - .suspend = usb_amradio_suspend, - .resume = usb_amradio_resume, - .reset_resume = usb_amradio_resume, - .id_table = usb_amradio_device_table, - .supports_autosuspend = 1, -}; - -/* switch on/off the radio. Send 8 bytes to device */ -static int amradio_set_mute(struct amradio_device *radio, char argument) +static int amradio_send_cmd(struct amradio_device *radio, u8 cmd, u8 arg, + u8 *extra, u8 extralen, bool reply) { int retval; int size; @@ -177,99 +154,92 @@ static int amradio_set_mute(struct amradio_device *radio, char argument) radio->buffer[0] = 0x00; radio->buffer[1] = 0x55; radio->buffer[2] = 0xaa; - radio->buffer[3] = 0x00; - radio->buffer[4] = AMRADIO_SET_MUTE; - radio->buffer[5] = argument; + radio->buffer[3] = extralen; + radio->buffer[4] = cmd; + radio->buffer[5] = arg; radio->buffer[6] = 0x00; - radio->buffer[7] = 0x00; + radio->buffer[7] = extra || reply ? 8 : 0; retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), - (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); + radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); if (retval < 0 || size != BUFFER_LENGTH) { - amradio_dev_warn(&radio->videodev.dev, "set mute failed\n"); - return retval; + if (video_is_registered(&radio->vdev)) + amradio_dev_warn(&radio->vdev.dev, + "cmd %02x failed\n", cmd); + return retval ? retval : -EIO; } + if (!extra && !reply) + return 0; - radio->muted = argument; + if (extra) { + memcpy(radio->buffer, extra, extralen); + memset(radio->buffer + extralen, 0, 8 - extralen); + retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), + radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); + } else { + memset(radio->buffer, 0, 8); + retval = usb_bulk_msg(radio->usbdev, usb_rcvbulkpipe(radio->usbdev, 0x81), + radio->buffer, BUFFER_LENGTH, &size, USB_TIMEOUT); + } + if (retval == 0 && size == BUFFER_LENGTH) + return 0; + if (video_is_registered(&radio->vdev) && cmd != AMRADIO_GET_READY_FLAG) + amradio_dev_warn(&radio->vdev.dev, "follow-up to cmd %02x failed\n", cmd); + return retval ? retval : -EIO; +} - return retval; +/* switch on/off the radio. Send 8 bytes to device */ +static int amradio_set_mute(struct amradio_device *radio, bool mute) +{ + int ret = amradio_send_cmd(radio, + AMRADIO_SET_MUTE, mute, NULL, 0, false); + + if (!ret) + radio->muted = mute; + return ret; } /* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */ -static int amradio_setfreq(struct amradio_device *radio, int freq) +static int amradio_set_freq(struct amradio_device *radio, int freq) { - int retval; - int size; unsigned short freq_send = 0x10 + (freq >> 3) / 25; - - radio->buffer[0] = 0x00; - radio->buffer[1] = 0x55; - radio->buffer[2] = 0xaa; - radio->buffer[3] = 0x03; - radio->buffer[4] = AMRADIO_SET_FREQ; - radio->buffer[5] = 0x00; - radio->buffer[6] = 0x00; - radio->buffer[7] = 0x08; - - 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) - goto out_err; + u8 buf[3]; + int retval; /* frequency is calculated from freq_send and placed in first 2 bytes */ - radio->buffer[0] = (freq_send >> 8) & 0xff; - radio->buffer[1] = freq_send & 0xff; - radio->buffer[2] = 0x01; - radio->buffer[3] = 0x00; - radio->buffer[4] = 0x00; - /* 5 and 6 bytes of buffer already = 0x00 */ - radio->buffer[7] = 0x00; - - 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) - goto out_err; + buf[0] = (freq_send >> 8) & 0xff; + buf[1] = freq_send & 0xff; + buf[2] = 0x01; + retval = amradio_send_cmd(radio, AMRADIO_SET_FREQ, 0, buf, 3, false); + if (retval) + return retval; radio->curfreq = freq; - goto out; - -out_err: - amradio_dev_warn(&radio->videodev.dev, "set frequency failed\n"); -out: - return retval; + msleep(40); + return 0; } -static int amradio_set_stereo(struct amradio_device *radio, char argument) +static int amradio_set_stereo(struct amradio_device *radio, bool stereo) { - int retval; - int size; + int ret = amradio_send_cmd(radio, + AMRADIO_SET_MONO, !stereo, NULL, 0, false); - radio->buffer[0] = 0x00; - radio->buffer[1] = 0x55; - radio->buffer[2] = 0xaa; - radio->buffer[3] = 0x00; - radio->buffer[4] = AMRADIO_SET_MONO; - radio->buffer[5] = argument; - radio->buffer[6] = 0x00; - radio->buffer[7] = 0x00; + if (!ret) + radio->stereo = stereo; + return ret; +} - retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2), - (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT); +static int amradio_get_stat(struct amradio_device *radio, bool *is_stereo, u32 *signal) +{ + int ret = amradio_send_cmd(radio, + AMRADIO_GET_SIGNAL, 0, NULL, 0, true); - if (retval < 0 || size != BUFFER_LENGTH) { - amradio_dev_warn(&radio->videodev.dev, "set stereo failed\n"); - return retval; - } - - if (argument == WANT_STEREO) - radio->stereo = 1; - else - radio->stereo = 0; - - return retval; + if (ret) + return ret; + *is_stereo = radio->buffer[2] >> 7; + *signal = (radio->buffer[3] & 0xf0) << 8; + return 0; } /* Handle unplugging the device. @@ -282,25 +252,26 @@ static void usb_amradio_disconnect(struct usb_interface *intf) struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); - /* increase the device node's refcount */ - get_device(&radio->videodev.dev); + video_unregister_device(&radio->vdev); + amradio_set_mute(radio, true); + usb_set_intfdata(intf, NULL); v4l2_device_disconnect(&radio->v4l2_dev); - video_unregister_device(&radio->videodev); mutex_unlock(&radio->lock); - /* decrease the device node's refcount, allowing it to be released */ - put_device(&radio->videodev.dev); + v4l2_device_put(&radio->v4l2_dev); } /* vidioc_querycap - query device capabilities */ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { - struct amradio_device *radio = file->private_data; + struct amradio_device *radio = video_drvdata(file); strlcpy(v->driver, "radio-mr800", sizeof(v->driver)); strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card)); usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info)); - v->capabilities = V4L2_CAP_TUNER; + v->device_caps = V4L2_CAP_RADIO | V4L2_CAP_TUNER | + V4L2_CAP_HW_FREQ_SEEK; + v->capabilities = v->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -308,44 +279,34 @@ 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 = file->private_data; + struct amradio_device *radio = video_drvdata(file); + bool is_stereo = false; int retval; if (v->index > 0) return -EINVAL; -/* TODO: Add function which look is signal stereo or not - * amradio_getstat(radio); - */ - -/* we call amradio_set_stereo to set radio->stereo - * Honestly, amradio_getstat should cover this in future and - * amradio_set_stereo shouldn't be here - */ - retval = amradio_set_stereo(radio, WANT_STEREO); + v->signal = 0; + retval = amradio_get_stat(radio, &is_stereo, &v->signal); + if (retval) + return retval; strcpy(v->name, "FM"); v->type = V4L2_TUNER_RADIO; v->rangelow = FREQ_MIN * FREQ_MUL; v->rangehigh = FREQ_MAX * FREQ_MUL; - v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; - v->capability = V4L2_TUNER_CAP_LOW; - if (radio->stereo) - v->audmode = V4L2_TUNER_MODE_STEREO; - else - 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 retval; + v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO; + v->rxsubchans = is_stereo ? V4L2_TUNER_SUB_STEREO : V4L2_TUNER_SUB_MONO; + v->audmode = radio->stereo ? + V4L2_TUNER_MODE_STEREO : V4L2_TUNER_MODE_MONO; + return 0; } /* vidioc_s_tuner - set tuner attributes */ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v) { - struct amradio_device *radio = file->private_data; - int retval = -EINVAL; + struct amradio_device *radio = video_drvdata(file); if (v->index > 0) return -EINVAL; @@ -353,34 +314,31 @@ static int vidioc_s_tuner(struct file *file, void *priv, /* mono/stereo selector */ switch (v->audmode) { case V4L2_TUNER_MODE_MONO: - retval = amradio_set_stereo(radio, WANT_MONO); - break; - case V4L2_TUNER_MODE_STEREO: - retval = amradio_set_stereo(radio, WANT_STEREO); - break; + return amradio_set_stereo(radio, WANT_MONO); + default: + return amradio_set_stereo(radio, WANT_STEREO); } - - 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 = file->private_data; + struct amradio_device *radio = video_drvdata(file); - if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) + if (f->tuner != 0) return -EINVAL; - return amradio_setfreq(radio, f->frequency); + return amradio_set_freq(radio, clamp_t(unsigned, f->frequency, + FREQ_MIN * FREQ_MUL, FREQ_MAX * FREQ_MUL)); } /* 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 = file->private_data; + struct amradio_device *radio = video_drvdata(file); - if (f->tuner != 0) + if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO) return -EINVAL; f->type = V4L2_TUNER_RADIO; f->frequency = radio->curfreq; @@ -388,148 +346,101 @@ static int vidioc_g_frequency(struct file *file, void *priv, return 0; } -/* vidioc_queryctrl - enumerate control items */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int vidioc_s_hw_freq_seek(struct file *file, void *priv, + struct v4l2_hw_freq_seek *seek) { - switch (qc->id) { - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); + static u8 buf[8] = { + 0x3d, 0x32, 0x0f, 0x08, 0x3d, 0x32, 0x0f, 0x08 + }; + struct amradio_device *radio = video_drvdata(file); + unsigned long timeout; + int retval; + + if (seek->tuner != 0 || !seek->wrap_around) + return -EINVAL; + + retval = amradio_send_cmd(radio, + AMRADIO_SET_SEARCH_LVL, 0, buf, 8, false); + if (retval) + return retval; + amradio_set_freq(radio, radio->curfreq); + retval = amradio_send_cmd(radio, + seek->seek_upward ? AMRADIO_SET_SEARCH_UP : AMRADIO_SET_SEARCH_DOWN, + 0, NULL, 0, false); + if (retval) + return retval; + timeout = jiffies + msecs_to_jiffies(30000); + for (;;) { + if (time_after(jiffies, timeout)) { + retval = -EAGAIN; + break; + } + if (schedule_timeout_interruptible(msecs_to_jiffies(10))) { + retval = -ERESTARTSYS; + break; + } + retval = amradio_send_cmd(radio, AMRADIO_GET_READY_FLAG, + 0, NULL, 0, true); + if (retval) + continue; + amradio_send_cmd(radio, AMRADIO_GET_FREQ, 0, NULL, 0, true); + if (radio->buffer[1] || radio->buffer[2]) { + radio->curfreq = (radio->buffer[1] << 8) | radio->buffer[2]; + radio->curfreq = (radio->curfreq - 0x10) * 200; + amradio_send_cmd(radio, AMRADIO_STOP_SEARCH, + 0, NULL, 0, false); + amradio_set_freq(radio, radio->curfreq); + retval = 0; + break; + } } - - return -EINVAL; -} - -/* vidioc_g_ctrl - get the value of a control */ -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct amradio_device *radio = file->private_data; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - ctrl->value = radio->muted; - return 0; - } - - return -EINVAL; -} - -/* vidioc_s_ctrl - set the value of a control */ -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct amradio_device *radio = file->private_data; - int retval = -EINVAL; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_MUTE: - if (ctrl->value) - retval = amradio_set_mute(radio, AMRADIO_STOP); - else - retval = amradio_set_mute(radio, AMRADIO_START); - - break; - } - + amradio_send_cmd(radio, AMRADIO_STOP_SEARCH, 0, NULL, 0, false); + amradio_set_freq(radio, radio->curfreq); return retval; } -/* vidioc_g_audio - get audio attributes */ -static int vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *a) +static int usb_amradio_s_ctrl(struct v4l2_ctrl *ctrl) { - if (a->index > 1) - return -EINVAL; + struct amradio_device *radio = + container_of(ctrl->handler, struct amradio_device, hdl); - strcpy(a->name, "Radio"); - a->capability = V4L2_AUDCAP_STEREO; - return 0; -} + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + return amradio_set_mute(radio, ctrl->val); + } -/* vidioc_s_audio - set audio attributes */ -static int vidioc_s_audio(struct file *file, void *priv, - struct v4l2_audio *a) -{ - if (a->index != 0) - return -EINVAL; - return 0; -} - -/* vidioc_g_input - get input */ -static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -/* vidioc_s_input - set input */ -static int vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - if (i != 0) - return -EINVAL; - return 0; + return -EINVAL; } static int usb_amradio_init(struct amradio_device *radio) { int retval; - retval = amradio_set_mute(radio, AMRADIO_STOP); + retval = amradio_set_mute(radio, true); if (retval) goto out_err; - - retval = amradio_set_stereo(radio, WANT_STEREO); + retval = amradio_set_stereo(radio, true); if (retval) goto out_err; - - radio->initialized = 1; - goto out; + retval = amradio_set_freq(radio, radio->curfreq); + if (retval) + goto out_err; + return 0; out_err: - amradio_dev_err(&radio->videodev.dev, "initialization failed\n"); -out: + amradio_dev_err(&radio->vdev.dev, "initialization failed\n"); return retval; } -/* open device - amradio_start() and amradio_setfreq() */ -static int usb_amradio_open(struct file *file) -{ - struct amradio_device *radio = video_drvdata(file); - int retval; - - file->private_data = radio; - retval = usb_autopm_get_interface(radio->intf); - if (retval) - return retval; - - if (unlikely(!radio->initialized)) { - retval = usb_amradio_init(radio); - if (retval) - usb_autopm_put_interface(radio->intf); - } - return retval; -} - -/*close device */ -static int usb_amradio_close(struct file *file) -{ - struct amradio_device *radio = file->private_data; - - if (video_is_registered(&radio->videodev)) - usb_autopm_put_interface(radio->intf); - return 0; -} - /* 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 = to_amradio_dev(usb_get_intfdata(intf)); mutex_lock(&radio->lock); - if (!radio->muted && radio->initialized) { - amradio_set_mute(radio, AMRADIO_STOP); - radio->muted = 0; + if (!radio->muted) { + amradio_set_mute(radio, true); + radio->muted = false; } mutex_unlock(&radio->lock); @@ -543,31 +454,28 @@ static int usb_amradio_resume(struct usb_interface *intf) struct amradio_device *radio = to_amradio_dev(usb_get_intfdata(intf)); 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); + amradio_set_stereo(radio, radio->stereo); + amradio_set_freq(radio, radio->curfreq); if (!radio->muted) - amradio_set_mute(radio, AMRADIO_START); + amradio_set_mute(radio, false); -unlock: mutex_unlock(&radio->lock); dev_info(&intf->dev, "coming out of suspend..\n"); return 0; } +static const struct v4l2_ctrl_ops usb_amradio_ctrl_ops = { + .s_ctrl = usb_amradio_s_ctrl, +}; + /* File system interface */ static const struct v4l2_file_operations usb_amradio_fops = { .owner = THIS_MODULE, - .open = usb_amradio_open, - .release = usb_amradio_close, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, }; @@ -577,20 +485,19 @@ static const struct v4l2_ioctl_ops usb_amradio_ioctl_ops = { .vidioc_s_tuner = vidioc_s_tuner, .vidioc_g_frequency = vidioc_g_frequency, .vidioc_s_frequency = vidioc_s_frequency, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_g_audio = vidioc_g_audio, - .vidioc_s_audio = vidioc_s_audio, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, + .vidioc_s_hw_freq_seek = vidioc_s_hw_freq_seek, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; -static void usb_amradio_video_device_release(struct video_device *videodev) +static void usb_amradio_release(struct v4l2_device *v4l2_dev) { - struct amradio_device *radio = video_get_drvdata(videodev); + struct amradio_device *radio = to_amradio_dev(v4l2_dev); /* free rest memory */ + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); kfree(radio->buffer); kfree(radio); } @@ -624,23 +531,38 @@ static int usb_amradio_probe(struct usb_interface *intf, goto err_v4l2; } + v4l2_ctrl_handler_init(&radio->hdl, 1); + v4l2_ctrl_new_std(&radio->hdl, &usb_amradio_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&intf->dev, "couldn't register control\n"); + goto err_ctrl; + } mutex_init(&radio->lock); - 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; - radio->videodev.lock = &radio->lock; + radio->v4l2_dev.ctrl_handler = &radio->hdl; + radio->v4l2_dev.release = usb_amradio_release; + strlcpy(radio->vdev.name, radio->v4l2_dev.name, + sizeof(radio->vdev.name)); + radio->vdev.v4l2_dev = &radio->v4l2_dev; + radio->vdev.fops = &usb_amradio_fops; + radio->vdev.ioctl_ops = &usb_amradio_ioctl_ops; + radio->vdev.release = video_device_release_empty; + radio->vdev.lock = &radio->lock; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->vdev.flags); radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; + usb_set_intfdata(intf, &radio->v4l2_dev); radio->curfreq = 95.16 * FREQ_MUL; - video_set_drvdata(&radio->videodev, radio); + video_set_drvdata(&radio->vdev, radio); + retval = usb_amradio_init(radio); + if (retval) + goto err_vdev; - retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, + retval = video_register_device(&radio->vdev, VFL_TYPE_RADIO, radio_nr); if (retval < 0) { dev_err(&intf->dev, "could not register video device\n"); @@ -650,6 +572,8 @@ static int usb_amradio_probe(struct usb_interface *intf, return 0; err_vdev: + v4l2_ctrl_handler_free(&radio->hdl); +err_ctrl: v4l2_device_unregister(&radio->v4l2_dev); err_v4l2: kfree(radio->buffer); @@ -659,4 +583,24 @@ err: return retval; } +/* USB Device ID List */ +static struct usb_device_id usb_amradio_device_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(USB_AMRADIO_VENDOR, USB_AMRADIO_PRODUCT, + USB_CLASS_HID, 0, 0) }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, usb_amradio_device_table); + +/* USB subsystem interface */ +static struct usb_driver usb_amradio_driver = { + .name = MR800_DRIVER_NAME, + .probe = usb_amradio_probe, + .disconnect = usb_amradio_disconnect, + .suspend = usb_amradio_suspend, + .resume = usb_amradio_resume, + .reset_resume = usb_amradio_resume, + .id_table = usb_amradio_device_table, +}; + module_usb_driver(usb_amradio_driver); diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c index b275c5d0fe9a..b1f844c64fde 100644 --- a/drivers/media/radio/radio-rtrack2.c +++ b/drivers/media/radio/radio-rtrack2.c @@ -17,6 +17,7 @@ #include /* kernel radio structs */ #include #include /* outb, outb_p */ +#include #include #include #include "radio-isa.h" diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c index 22c5743bf9db..a81d723b8c77 100644 --- a/drivers/media/radio/radio-sf16fmi.c +++ b/drivers/media/radio/radio-sf16fmi.c @@ -1,4 +1,4 @@ -/* SF16-FMI and SF16-FMP radio driver for Linux radio support +/* SF16-FMI, SF16-FMP and SF16-FMD radio driver for Linux radio support * heavily based on rtrack driver... * (c) 1997 M. Kirkwood * (c) 1998 Petr Vandrovec, vandrove@vc.cvut.cz @@ -11,7 +11,7 @@ * * Frequency control is done digitally -- ie out(port,encodefreq(95.8)); * No volume control - only mute/unmute - you have to use line volume - * control on SB-part of SF16-FMI/SF16-FMP + * control on SB-part of SF16-FMI/SF16-FMP/SF16-FMD * * Converted to V4L2 API by Mauro Carvalho Chehab */ @@ -29,7 +29,7 @@ #include MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood"); -MODULE_DESCRIPTION("A driver for the SF16-FMI and SF16-FMP radio."); +MODULE_DESCRIPTION("A driver for the SF16-FMI, SF16-FMP and SF16-FMD radio."); MODULE_LICENSE("GPL"); MODULE_VERSION("0.0.3"); @@ -37,7 +37,7 @@ static int io = -1; static int radio_nr = -1; module_param(io, int, 0); -MODULE_PARM_DESC(io, "I/O address of the SF16-FMI or SF16-FMP card (0x284 or 0x384)"); +MODULE_PARM_DESC(io, "I/O address of the SF16-FMI/SF16-FMP/SF16-FMD card (0x284 or 0x384)"); module_param(radio_nr, int, 0); struct fmi @@ -130,7 +130,7 @@ static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *v) { strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver)); - strlcpy(v->card, "SF16-FMx radio", sizeof(v->card)); + strlcpy(v->card, "SF16-FMI/FMP/FMD radio", sizeof(v->card)); strlcpy(v->bus_info, "ISA", sizeof(v->bus_info)); v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO; return 0; @@ -277,8 +277,12 @@ static const struct v4l2_ioctl_ops fmi_ioctl_ops = { /* ladis: this is my card. does any other types exist? */ static struct isapnp_device_id id_table[] __devinitdata = { + /* SF16-FMI */ { ISAPNP_ANY_ID, ISAPNP_ANY_ID, ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad10), 0}, + /* SF16-FMD */ + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('M','F','R'), ISAPNP_FUNCTION(0xad12), 0}, { ISAPNP_CARD_END, }, }; diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c index 7c69214334bf..52b8011f1b23 100644 --- a/drivers/media/radio/radio-sf16fmr2.c +++ b/drivers/media/radio/radio-sf16fmr2.c @@ -1,4 +1,4 @@ -/* SF16-FMR2 radio driver for Linux +/* SF16-FMR2 and SF16-FMD2 radio driver for Linux * Copyright (c) 2011 Ondrej Zary * * Original driver was (c) 2000-2002 Ziglio Frediano, freddy77@angelfire.com @@ -13,15 +13,19 @@ #include /* request_region */ #include /* outb, outb_p */ #include +#include #include MODULE_AUTHOR("Ondrej Zary"); -MODULE_DESCRIPTION("MediaForte SF16-FMR2 FM radio card driver"); +MODULE_DESCRIPTION("MediaForte SF16-FMR2 and SF16-FMD2 FM radio card driver"); MODULE_LICENSE("GPL"); -static int radio_nr = -1; -module_param(radio_nr, int, 0444); -MODULE_PARM_DESC(radio_nr, "Radio device number"); +/* these cards can only use two different ports (0x384 and 0x284) */ +#define FMR2_MAX 2 + +static int radio_nr[FMR2_MAX] = { [0 ... (FMR2_MAX - 1)] = -1 }; +module_param_array(radio_nr, int, NULL, 0444); +MODULE_PARM_DESC(radio_nr, "Radio device numbers"); struct fmr2 { int io; @@ -29,9 +33,15 @@ struct fmr2 { struct snd_tea575x tea; struct v4l2_ctrl *volume; struct v4l2_ctrl *balance; + bool is_fmd2; }; -/* the port is hardwired so no need to support multiple cards */ +static int num_fmr2_cards; +static struct fmr2 *fmr2_cards[FMR2_MAX]; +static bool isa_registered; +static bool pnp_registered; + +/* the port is hardwired on SF16-FMR2 */ #define FMR2_PORT 0x384 /* TEA575x tuner pins */ @@ -174,7 +184,8 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea) { struct fmr2 *fmr2 = tea->private_data; - if (inb(fmr2->io) & FMR2_HASVOL) { + /* FMR2 can have volume control, FMD2 can't (uses SB16 mixer) */ + if (!fmr2->is_fmd2 && inb(fmr2->io) & FMR2_HASVOL) { fmr2->volume = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_VOLUME, 0, 68, 2, 56); fmr2->balance = v4l2_ctrl_new_std(&tea->ctrl_handler, &fmr2_ctrl_ops, V4L2_CID_AUDIO_BALANCE, -68, 68, 2, 0); if (tea->ctrl_handler.error) { @@ -186,22 +197,28 @@ static int fmr2_tea_ext_init(struct snd_tea575x *tea) return 0; } -static int __devinit fmr2_probe(struct device *pdev, unsigned int dev) +static struct pnp_device_id fmr2_pnp_ids[] __devinitdata = { + { .id = "MFRad13" }, /* tuner subdevice of SF16-FMD2 */ + { .id = "" } +}; +MODULE_DEVICE_TABLE(pnp, fmr2_pnp_ids); + +static int __devinit fmr2_probe(struct fmr2 *fmr2, struct device *pdev, int io) { - struct fmr2 *fmr2; - int err; + int err, i; + char *card_name = fmr2->is_fmd2 ? "SF16-FMD2" : "SF16-FMR2"; - fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); - if (fmr2 == NULL) - return -ENOMEM; + /* avoid errors if a card was already registered at given port */ + for (i = 0; i < num_fmr2_cards; i++) + if (io == fmr2_cards[i]->io) + return -EBUSY; - strlcpy(fmr2->v4l2_dev.name, dev_name(pdev), - sizeof(fmr2->v4l2_dev.name)); - fmr2->io = FMR2_PORT; + strlcpy(fmr2->v4l2_dev.name, "radio-sf16fmr2", + sizeof(fmr2->v4l2_dev.name)), + fmr2->io = io; if (!request_region(fmr2->io, 2, fmr2->v4l2_dev.name)) { printk(KERN_ERR "radio-sf16fmr2: I/O port 0x%x already in use\n", fmr2->io); - kfree(fmr2); return -EBUSY; } @@ -210,56 +227,121 @@ static int __devinit fmr2_probe(struct device *pdev, unsigned int dev) if (err < 0) { v4l2_err(&fmr2->v4l2_dev, "Could not register v4l2_device\n"); release_region(fmr2->io, 2); - kfree(fmr2); return err; } fmr2->tea.v4l2_dev = &fmr2->v4l2_dev; fmr2->tea.private_data = fmr2; - fmr2->tea.radio_nr = radio_nr; + fmr2->tea.radio_nr = radio_nr[num_fmr2_cards]; fmr2->tea.ops = &fmr2_tea_ops; fmr2->tea.ext_init = fmr2_tea_ext_init; - strlcpy(fmr2->tea.card, "SF16-FMR2", sizeof(fmr2->tea.card)); - snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "ISA:%s", - fmr2->v4l2_dev.name); + strlcpy(fmr2->tea.card, card_name, sizeof(fmr2->tea.card)); + snprintf(fmr2->tea.bus_info, sizeof(fmr2->tea.bus_info), "%s:%s", + fmr2->is_fmd2 ? "PnP" : "ISA", dev_name(pdev)); if (snd_tea575x_init(&fmr2->tea)) { printk(KERN_ERR "radio-sf16fmr2: Unable to detect TEA575x tuner\n"); release_region(fmr2->io, 2); - kfree(fmr2); return -ENODEV; } - printk(KERN_INFO "radio-sf16fmr2: SF16-FMR2 radio card at 0x%x.\n", fmr2->io); + printk(KERN_INFO "radio-sf16fmr2: %s radio card at 0x%x.\n", + card_name, fmr2->io); return 0; } -static int __exit fmr2_remove(struct device *pdev, unsigned int dev) +static int __devinit fmr2_isa_match(struct device *pdev, unsigned int ndev) { - struct fmr2 *fmr2 = dev_get_drvdata(pdev); + struct fmr2 *fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); + if (!fmr2) + return 0; + if (fmr2_probe(fmr2, pdev, FMR2_PORT)) { + kfree(fmr2); + return 0; + } + dev_set_drvdata(pdev, fmr2); + fmr2_cards[num_fmr2_cards++] = fmr2; + + return 1; +} + +static int __devinit fmr2_pnp_probe(struct pnp_dev *pdev, + const struct pnp_device_id *id) +{ + int ret; + struct fmr2 *fmr2 = kzalloc(sizeof(*fmr2), GFP_KERNEL); + if (!fmr2) + return -ENOMEM; + + fmr2->is_fmd2 = true; + ret = fmr2_probe(fmr2, &pdev->dev, pnp_port_start(pdev, 0)); + if (ret) { + kfree(fmr2); + return ret; + } + pnp_set_drvdata(pdev, fmr2); + fmr2_cards[num_fmr2_cards++] = fmr2; + + return 0; +} + +static void __devexit fmr2_remove(struct fmr2 *fmr2) +{ snd_tea575x_exit(&fmr2->tea); release_region(fmr2->io, 2); v4l2_device_unregister(&fmr2->v4l2_dev); kfree(fmr2); +} + +static int __devexit fmr2_isa_remove(struct device *pdev, unsigned int ndev) +{ + fmr2_remove(dev_get_drvdata(pdev)); + dev_set_drvdata(pdev, NULL); + return 0; } -struct isa_driver fmr2_driver = { - .probe = fmr2_probe, - .remove = fmr2_remove, +static void __devexit fmr2_pnp_remove(struct pnp_dev *pdev) +{ + fmr2_remove(pnp_get_drvdata(pdev)); + pnp_set_drvdata(pdev, NULL); +} + +struct isa_driver fmr2_isa_driver = { + .match = fmr2_isa_match, + .remove = __devexit_p(fmr2_isa_remove), .driver = { .name = "radio-sf16fmr2", }, }; +struct pnp_driver fmr2_pnp_driver = { + .name = "radio-sf16fmr2", + .id_table = fmr2_pnp_ids, + .probe = fmr2_pnp_probe, + .remove = __devexit_p(fmr2_pnp_remove), +}; + static int __init fmr2_init(void) { - return isa_register_driver(&fmr2_driver, 1); + int ret; + + ret = pnp_register_driver(&fmr2_pnp_driver); + if (!ret) + pnp_registered = true; + ret = isa_register_driver(&fmr2_isa_driver, 1); + if (!ret) + isa_registered = true; + + return (pnp_registered || isa_registered) ? 0 : ret; } static void __exit fmr2_exit(void) { - isa_unregister_driver(&fmr2_driver); + if (pnp_registered) + pnp_unregister_driver(&fmr2_pnp_driver); + if (isa_registered) + isa_unregister_driver(&fmr2_isa_driver); } module_init(fmr2_init); diff --git a/drivers/media/radio/radio-timb.c b/drivers/media/radio/radio-timb.c index 5d9a90ac3a1c..7052adc0c0b0 100644 --- a/drivers/media/radio/radio-timb.c +++ b/drivers/media/radio/radio-timb.c @@ -223,7 +223,7 @@ static struct platform_driver timbradio_platform_driver = { .owner = THIS_MODULE, }, .probe = timbradio_probe, - .remove = timbradio_remove, + .remove = __devexit_p(timbradio_remove), }; module_platform_driver(timbradio_platform_driver); diff --git a/drivers/media/radio/saa7706h.c b/drivers/media/radio/saa7706h.c index 9474706350f8..bb953ef75f61 100644 --- a/drivers/media/radio/saa7706h.c +++ b/drivers/media/radio/saa7706h.c @@ -430,7 +430,7 @@ static struct i2c_driver saa7706h_driver = { .name = DRIVER_NAME, }, .probe = saa7706h_probe, - .remove = saa7706h_remove, + .remove = __devexit_p(saa7706h_remove), .id_table = saa7706h_id, }; diff --git a/drivers/media/radio/si470x/radio-si470x-common.c b/drivers/media/radio/si470x/radio-si470x-common.c index 0e740c98786c..969cf494d85b 100644 --- a/drivers/media/radio/si470x/radio-si470x-common.c +++ b/drivers/media/radio/si470x/radio-si470x-common.c @@ -196,9 +196,9 @@ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan) } if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - dev_warn(&radio->videodev->dev, "tune does not complete\n"); + dev_warn(&radio->videodev.dev, "tune does not complete\n"); if (timed_out) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "tune timed out after %u ms\n", tune_timeout); stop: @@ -262,7 +262,7 @@ static int si470x_get_freq(struct si470x_device *radio, unsigned int *freq) */ int si470x_set_freq(struct si470x_device *radio, unsigned int freq) { - unsigned int spacing, band_bottom; + unsigned int spacing, band_bottom, band_top; unsigned short chan; /* Spacing (kHz) */ @@ -278,19 +278,26 @@ int si470x_set_freq(struct si470x_device *radio, unsigned int freq) spacing = 0.050 * FREQ_MUL; break; }; - /* Bottom of Band (MHz) */ + /* Bottom/Top of Band (MHz) */ switch ((radio->registers[SYSCONFIG2] & SYSCONFIG2_BAND) >> 6) { /* 0: 87.5 - 108 MHz (USA, Europe) */ case 0: - band_bottom = 87.5 * FREQ_MUL; break; + band_bottom = 87.5 * FREQ_MUL; + band_top = 108 * FREQ_MUL; + break; /* 1: 76 - 108 MHz (Japan wide band) */ default: - band_bottom = 76 * FREQ_MUL; break; + band_bottom = 76 * FREQ_MUL; + band_top = 108 * FREQ_MUL; + break; /* 2: 76 - 90 MHz (Japan) */ case 2: - band_bottom = 76 * FREQ_MUL; break; + band_bottom = 76 * FREQ_MUL; + band_top = 90 * FREQ_MUL; + break; }; + freq = clamp(freq, band_bottom, band_top); /* Chan = [ Freq (Mhz) - Bottom of Band (MHz) ] / Spacing (kHz) */ chan = (freq - band_bottom) / spacing; @@ -320,7 +327,7 @@ static int si470x_set_seek(struct si470x_device *radio, radio->registers[POWERCFG] &= ~POWERCFG_SEEKUP; retval = si470x_set_register(radio, POWERCFG); if (retval < 0) - goto done; + return retval; /* currently I2C driver only uses interrupt way to seek */ if (radio->stci_enabled) { @@ -344,24 +351,19 @@ static int si470x_set_seek(struct si470x_device *radio, } if ((radio->registers[STATUSRSSI] & STATUSRSSI_STC) == 0) - dev_warn(&radio->videodev->dev, "seek does not complete\n"); + dev_warn(&radio->videodev.dev, "seek does not complete\n"); if (radio->registers[STATUSRSSI] & STATUSRSSI_SF) - dev_warn(&radio->videodev->dev, + dev_warn(&radio->videodev.dev, "seek failed / band limit reached\n"); - if (timed_out) - dev_warn(&radio->videodev->dev, - "seek timed out after %u ms\n", seek_timeout); stop: /* stop seeking */ radio->registers[POWERCFG] &= ~POWERCFG_SEEK; retval = si470x_set_register(radio, POWERCFG); -done: /* try again, if timed out */ - if ((retval == 0) && timed_out) - retval = -EAGAIN; - + if (retval == 0 && timed_out) + return -EAGAIN; return retval; } @@ -463,7 +465,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, unsigned int block_count = 0; /* switch on rds reception */ - mutex_lock(&radio->lock); if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) si470x_rds_on(radio); @@ -505,7 +506,6 @@ static ssize_t si470x_fops_read(struct file *file, char __user *buf, } done: - mutex_unlock(&radio->lock); return retval; } @@ -517,19 +517,19 @@ static unsigned int si470x_fops_poll(struct file *file, struct poll_table_struct *pts) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; + unsigned long req_events = poll_requested_events(pts); + int retval = v4l2_ctrl_poll(file, pts); - /* switch on rds reception */ + if (req_events & (POLLIN | POLLRDNORM)) { + /* switch on rds reception */ + if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) + si470x_rds_on(radio); - mutex_lock(&radio->lock); - if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) - si470x_rds_on(radio); - mutex_unlock(&radio->lock); + poll_wait(file, &radio->read_queue, pts); - poll_wait(file, &radio->read_queue, pts); - - if (radio->rd_index != radio->wr_index) - retval = POLLIN | POLLRDNORM; + if (radio->rd_index != radio->wr_index) + retval |= POLLIN | POLLRDNORM; + } return retval; } @@ -553,134 +553,26 @@ static const struct v4l2_file_operations si470x_fops = { * Video4Linux Interface **************************************************************************/ -/* - * si470x_vidioc_queryctrl - enumerate control items - */ -static int si470x_vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) + +static int si470x_s_ctrl(struct v4l2_ctrl *ctrl) { - struct si470x_device *radio = video_drvdata(file); - int retval = -EINVAL; - - /* abort if qc->id is below V4L2_CID_BASE */ - if (qc->id < V4L2_CID_BASE) - goto done; - - /* search video control */ - switch (qc->id) { - case V4L2_CID_AUDIO_VOLUME: - return v4l2_ctrl_query_fill(qc, 0, 15, 1, 15); - case V4L2_CID_AUDIO_MUTE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1); - } - - /* disable unsupported base controls */ - /* to satisfy kradio and such apps */ - if ((retval == -EINVAL) && (qc->id < V4L2_CID_LASTP1)) { - qc->flags = V4L2_CTRL_FLAG_DISABLED; - retval = 0; - } - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "query controls failed with %d\n", retval); - return retval; -} - - -/* - * si470x_vidioc_g_ctrl - get the value of a control - */ -static int si470x_vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - - switch (ctrl->id) { - case V4L2_CID_AUDIO_VOLUME: - ctrl->value = radio->registers[SYSCONFIG2] & - SYSCONFIG2_VOLUME; - break; - case V4L2_CID_AUDIO_MUTE: - ctrl->value = ((radio->registers[POWERCFG] & - POWERCFG_DMUTE) == 0) ? 1 : 0; - break; - default: - retval = -EINVAL; - } - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "get control failed with %d\n", retval); - - mutex_unlock(&radio->lock); - return retval; -} - - -/* - * si470x_vidioc_s_ctrl - set the value of a control - */ -static int si470x_vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; + struct si470x_device *radio = + container_of(ctrl->handler, struct si470x_device, hdl); switch (ctrl->id) { case V4L2_CID_AUDIO_VOLUME: radio->registers[SYSCONFIG2] &= ~SYSCONFIG2_VOLUME; - radio->registers[SYSCONFIG2] |= ctrl->value; - retval = si470x_set_register(radio, SYSCONFIG2); - break; + radio->registers[SYSCONFIG2] |= ctrl->val; + return si470x_set_register(radio, SYSCONFIG2); case V4L2_CID_AUDIO_MUTE: - if (ctrl->value == 1) + if (ctrl->val) radio->registers[POWERCFG] &= ~POWERCFG_DMUTE; else radio->registers[POWERCFG] |= POWERCFG_DMUTE; - retval = si470x_set_register(radio, POWERCFG); - break; + return si470x_set_register(radio, POWERCFG); default: - retval = -EINVAL; + return -EINVAL; } - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "set control failed with %d\n", retval); - mutex_unlock(&radio->lock); - return retval; -} - - -/* - * si470x_vidioc_g_audio - get audio attributes - */ -static int si470x_vidioc_g_audio(struct file *file, void *priv, - struct v4l2_audio *audio) -{ - /* driver constants */ - audio->index = 0; - strcpy(audio->name, "Radio"); - audio->capability = V4L2_AUDCAP_STEREO; - audio->mode = 0; - - return 0; } @@ -691,22 +583,14 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; + int retval; - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - - if (tuner->index != 0) { - retval = -EINVAL; - goto done; - } + if (tuner->index != 0) + return -EINVAL; retval = si470x_get_register(radio, STATUSRSSI); if (retval < 0) - goto done; + return retval; /* driver constants */ strcpy(tuner->name, "FM"); @@ -737,7 +621,7 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, if ((radio->registers[STATUSRSSI] & STATUSRSSI_ST) == 0) tuner->rxsubchans = V4L2_TUNER_SUB_MONO; else - tuner->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO; + tuner->rxsubchans = V4L2_TUNER_SUB_STEREO; /* If there is a reliable method of detecting an RDS channel, then this code should check for that before setting this RDS subchannel. */ @@ -754,16 +638,13 @@ static int si470x_vidioc_g_tuner(struct file *file, void *priv, tuner->signal = (radio->registers[STATUSRSSI] & STATUSRSSI_RSSI); /* the ideal factor is 0xffff/75 = 873,8 */ tuner->signal = (tuner->signal * 873) + (8 * tuner->signal / 10); + if (tuner->signal > 0xffff) + tuner->signal = 0xffff; /* automatic frequency control: -1: freq to low, 1 freq to high */ /* AFCRL does only indicate that freq. differs, not if too low/high */ tuner->afc = (radio->registers[STATUSRSSI] & STATUSRSSI_AFCRL) ? 1 : 0; -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "get tuner failed with %d\n", retval); - mutex_unlock(&radio->lock); return retval; } @@ -775,16 +656,9 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *tuner) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; if (tuner->index != 0) - goto done; + return -EINVAL; /* mono/stereo selector */ switch (tuner->audmode) { @@ -792,20 +666,12 @@ static int si470x_vidioc_s_tuner(struct file *file, void *priv, radio->registers[POWERCFG] |= POWERCFG_MONO; /* force mono */ break; case V4L2_TUNER_MODE_STEREO: + default: radio->registers[POWERCFG] &= ~POWERCFG_MONO; /* try stereo */ break; - default: - goto done; } - retval = si470x_set_register(radio, POWERCFG); - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "set tuner failed with %d\n", retval); - mutex_unlock(&radio->lock); - return retval; + return si470x_set_register(radio, POWERCFG); } @@ -816,28 +682,12 @@ static int si470x_vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - /* safety checks */ - mutex_lock(&radio->lock); - retval = si470x_disconnect_check(radio); - if (retval) - goto done; - - if (freq->tuner != 0) { - retval = -EINVAL; - goto done; - } + if (freq->tuner != 0) + return -EINVAL; freq->type = V4L2_TUNER_RADIO; - retval = si470x_get_freq(radio, &freq->frequency); - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "get frequency failed with %d\n", retval); - mutex_unlock(&radio->lock); - return retval; + return si470x_get_freq(radio, &freq->frequency); } @@ -848,27 +698,11 @@ static int si470x_vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *freq) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; + if (freq->tuner != 0) + return -EINVAL; - if (freq->tuner != 0) { - retval = -EINVAL; - goto done; - } - - retval = si470x_set_freq(radio, freq->frequency); - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "set frequency failed with %d\n", retval); - mutex_unlock(&radio->lock); - return retval; + return si470x_set_freq(radio, freq->frequency); } @@ -879,44 +713,29 @@ static int si470x_vidioc_s_hw_freq_seek(struct file *file, void *priv, struct v4l2_hw_freq_seek *seek) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - mutex_lock(&radio->lock); - /* safety checks */ - retval = si470x_disconnect_check(radio); - if (retval) - goto done; + if (seek->tuner != 0) + return -EINVAL; - if (seek->tuner != 0) { - retval = -EINVAL; - goto done; - } - - retval = si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); - -done: - if (retval < 0) - dev_warn(&radio->videodev->dev, - "set hardware frequency seek failed with %d\n", retval); - mutex_unlock(&radio->lock); - return retval; + return si470x_set_seek(radio, seek->wrap_around, seek->seek_upward); } +const struct v4l2_ctrl_ops si470x_ctrl_ops = { + .s_ctrl = si470x_s_ctrl, +}; /* * si470x_ioctl_ops - video device ioctl operations */ static const struct v4l2_ioctl_ops si470x_ioctl_ops = { .vidioc_querycap = si470x_vidioc_querycap, - .vidioc_queryctrl = si470x_vidioc_queryctrl, - .vidioc_g_ctrl = si470x_vidioc_g_ctrl, - .vidioc_s_ctrl = si470x_vidioc_s_ctrl, - .vidioc_g_audio = si470x_vidioc_g_audio, .vidioc_g_tuner = si470x_vidioc_g_tuner, .vidioc_s_tuner = si470x_vidioc_s_tuner, .vidioc_g_frequency = si470x_vidioc_g_frequency, .vidioc_s_frequency = si470x_vidioc_s_frequency, .vidioc_s_hw_freq_seek = si470x_vidioc_s_hw_freq_seek, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; @@ -926,6 +745,6 @@ static const struct v4l2_ioctl_ops si470x_ioctl_ops = { struct video_device si470x_viddev_template = { .fops = &si470x_fops, .name = DRIVER_NAME, - .release = video_device_release, + .release = video_device_release_empty, .ioctl_ops = &si470x_ioctl_ops, }; diff --git a/drivers/media/radio/si470x/radio-si470x-i2c.c b/drivers/media/radio/si470x/radio-si470x-i2c.c index 9b546a5523f3..a80044c5874e 100644 --- a/drivers/media/radio/si470x/radio-si470x-i2c.c +++ b/drivers/media/radio/si470x/radio-si470x-i2c.c @@ -161,20 +161,6 @@ static int si470x_get_all_registers(struct si470x_device *radio) -/************************************************************************** - * General Driver Functions - DISCONNECT_CHECK - **************************************************************************/ - -/* - * si470x_disconnect_check - check whether radio disconnects - */ -int si470x_disconnect_check(struct si470x_device *radio) -{ - return 0; -} - - - /************************************************************************** * File Operations Interface **************************************************************************/ @@ -185,12 +171,12 @@ int si470x_disconnect_check(struct si470x_device *radio) int si470x_fops_open(struct file *file) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; + int retval = v4l2_fh_open(file); - mutex_lock(&radio->lock); - radio->users++; + if (retval) + return retval; - if (radio->users == 1) { + if (v4l2_fh_is_singular_file(file)) { /* start radio */ retval = si470x_start(radio); if (retval < 0) @@ -205,7 +191,8 @@ int si470x_fops_open(struct file *file) } done: - mutex_unlock(&radio->lock); + if (retval) + v4l2_fh_release(file); return retval; } @@ -216,21 +203,12 @@ done: int si470x_fops_release(struct file *file) { struct si470x_device *radio = video_drvdata(file); - int retval = 0; - /* safety check */ - if (!radio) - return -ENODEV; - - mutex_lock(&radio->lock); - radio->users--; - if (radio->users == 0) + if (v4l2_fh_is_singular_file(file)) /* stop radio */ - retval = si470x_stop(radio); + si470x_stop(radio); - mutex_unlock(&radio->lock); - - return retval; + return v4l2_fh_release(file); } @@ -371,32 +349,25 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, goto err_initial; } - radio->users = 0; radio->client = client; mutex_init(&radio->lock); - /* video device allocation and initialization */ - radio->videodev = video_device_alloc(); - if (!radio->videodev) { - retval = -ENOMEM; - goto err_radio; - } - memcpy(radio->videodev, &si470x_viddev_template, - sizeof(si470x_viddev_template)); - video_set_drvdata(radio->videodev, radio); + /* video device initialization */ + radio->videodev = si470x_viddev_template; + video_set_drvdata(&radio->videodev, radio); /* power up : need 110ms */ radio->registers[POWERCFG] = POWERCFG_ENABLE; if (si470x_set_register(radio, POWERCFG) < 0) { retval = -EIO; - goto err_video; + goto err_radio; } msleep(110); /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; - goto err_video; + goto err_radio; } dev_info(&client->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); @@ -427,7 +398,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); if (!radio->buffer) { retval = -EIO; - goto err_video; + goto err_radio; } /* rds buffer configuration */ @@ -447,7 +418,7 @@ static int __devinit si470x_i2c_probe(struct i2c_client *client, } /* register video device */ - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, + retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); if (retval) { dev_warn(&client->dev, "Could not register video device\n"); @@ -460,8 +431,6 @@ err_all: free_irq(client->irq, radio); err_rds: kfree(radio->buffer); -err_video: - video_device_release(radio->videodev); err_radio: kfree(radio); err_initial: @@ -477,7 +446,7 @@ static __devexit int si470x_i2c_remove(struct i2c_client *client) struct si470x_device *radio = i2c_get_clientdata(client); free_irq(client->irq, radio); - video_unregister_device(radio->videodev); + video_unregister_device(&radio->videodev); kfree(radio); return 0; diff --git a/drivers/media/radio/si470x/radio-si470x-usb.c b/drivers/media/radio/si470x/radio-si470x-usb.c index b7debb67932a..e9f638761296 100644 --- a/drivers/media/radio/si470x/radio-si470x-usb.c +++ b/drivers/media/radio/si470x/radio-si470x-usb.c @@ -366,23 +366,6 @@ static int si470x_get_scratch_page_versions(struct si470x_device *radio) -/************************************************************************** - * General Driver Functions - DISCONNECT_CHECK - **************************************************************************/ - -/* - * si470x_disconnect_check - check whether radio disconnects - */ -int si470x_disconnect_check(struct si470x_device *radio) -{ - if (radio->disconnected) - return -EIO; - else - return 0; -} - - - /************************************************************************** * RDS Driver Functions **************************************************************************/ @@ -414,9 +397,6 @@ static void si470x_int_in_callback(struct urb *urb) } } - /* safety checks */ - if (radio->disconnected) - return; if ((radio->registers[SYSCONFIG1] & SYSCONFIG1_RDS) == 0) goto resubmit; @@ -501,110 +481,28 @@ resubmit: } - -/************************************************************************** - * File Operations Interface - **************************************************************************/ - -/* - * si470x_fops_open - file open - */ int si470x_fops_open(struct file *file) { - struct si470x_device *radio = video_drvdata(file); - int retval; - - mutex_lock(&radio->lock); - radio->users++; - - retval = usb_autopm_get_interface(radio->intf); - if (retval < 0) { - radio->users--; - retval = -EIO; - goto done; - } - - if (radio->users == 1) { - /* start radio */ - retval = si470x_start(radio); - if (retval < 0) { - usb_autopm_put_interface(radio->intf); - goto done; - } - - /* initialize interrupt urb */ - usb_fill_int_urb(radio->int_in_urb, radio->usbdev, - usb_rcvintpipe(radio->usbdev, - radio->int_in_endpoint->bEndpointAddress), - radio->int_in_buffer, - le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), - si470x_int_in_callback, - radio, - radio->int_in_endpoint->bInterval); - - radio->int_in_running = 1; - mb(); - - retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); - if (retval) { - dev_info(&radio->intf->dev, - "submitting int urb failed (%d)\n", retval); - radio->int_in_running = 0; - usb_autopm_put_interface(radio->intf); - } - } - -done: - mutex_unlock(&radio->lock); - return retval; + return v4l2_fh_open(file); } - -/* - * si470x_fops_release - file release - */ int si470x_fops_release(struct file *file) { - struct si470x_device *radio = video_drvdata(file); - int retval = 0; - - /* safety check */ - if (!radio) { - retval = -ENODEV; - goto done; - } - - mutex_lock(&radio->lock); - radio->users--; - if (radio->users == 0) { - /* shutdown interrupt handler */ - if (radio->int_in_running) { - radio->int_in_running = 0; - if (radio->int_in_urb) - usb_kill_urb(radio->int_in_urb); - } - - if (radio->disconnected) { - video_unregister_device(radio->videodev); - kfree(radio->int_in_buffer); - kfree(radio->buffer); - mutex_unlock(&radio->lock); - kfree(radio); - goto done; - } - - /* cancel read processes */ - wake_up_interruptible(&radio->read_queue); - - /* stop radio */ - retval = si470x_stop(radio); - usb_autopm_put_interface(radio->intf); - } - mutex_unlock(&radio->lock); -done: - return retval; + return v4l2_fh_release(file); } +static void si470x_usb_release(struct v4l2_device *v4l2_dev) +{ + struct si470x_device *radio = + container_of(v4l2_dev, struct si470x_device, v4l2_dev); + + usb_free_urb(radio->int_in_urb); + v4l2_ctrl_handler_free(&radio->hdl); + v4l2_device_unregister(&radio->v4l2_dev); + kfree(radio->int_in_buffer); + kfree(radio->buffer); + kfree(radio); +} /************************************************************************** @@ -623,13 +521,45 @@ int si470x_vidioc_querycap(struct file *file, void *priv, strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card)); usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info)); - capability->capabilities = V4L2_CAP_HW_FREQ_SEEK | + capability->device_caps = V4L2_CAP_HW_FREQ_SEEK | V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_RDS_CAPTURE; - + capability->capabilities = capability->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } +static int si470x_start_usb(struct si470x_device *radio) +{ + int retval; + + /* start radio */ + retval = si470x_start(radio); + if (retval < 0) + return retval; + + v4l2_ctrl_handler_setup(&radio->hdl); + + /* initialize interrupt urb */ + usb_fill_int_urb(radio->int_in_urb, radio->usbdev, + usb_rcvintpipe(radio->usbdev, + radio->int_in_endpoint->bEndpointAddress), + radio->int_in_buffer, + le16_to_cpu(radio->int_in_endpoint->wMaxPacketSize), + si470x_int_in_callback, + radio, + radio->int_in_endpoint->bInterval); + + radio->int_in_running = 1; + mb(); + + retval = usb_submit_urb(radio->int_in_urb, GFP_KERNEL); + if (retval) { + dev_info(&radio->intf->dev, + "submitting int urb failed (%d)\n", retval); + radio->int_in_running = 0; + } + return retval; +} /************************************************************************** * USB Interface @@ -653,8 +583,6 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, retval = -ENOMEM; goto err_initial; } - radio->users = 0; - radio->disconnected = 0; radio->usbdev = interface_to_usbdev(intf); radio->intf = intf; mutex_init(&radio->lock); @@ -691,20 +619,35 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, goto err_intbuffer; } - /* video device allocation and initialization */ - radio->videodev = video_device_alloc(); - if (!radio->videodev) { - retval = -ENOMEM; + radio->v4l2_dev.release = si470x_usb_release; + retval = v4l2_device_register(&intf->dev, &radio->v4l2_dev); + if (retval < 0) { + dev_err(&intf->dev, "couldn't register v4l2_device\n"); goto err_urb; } - memcpy(radio->videodev, &si470x_viddev_template, - sizeof(si470x_viddev_template)); - video_set_drvdata(radio->videodev, radio); + + v4l2_ctrl_handler_init(&radio->hdl, 2); + v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + v4l2_ctrl_new_std(&radio->hdl, &si470x_ctrl_ops, + V4L2_CID_AUDIO_VOLUME, 0, 15, 1, 15); + if (radio->hdl.error) { + retval = radio->hdl.error; + dev_err(&intf->dev, "couldn't register control\n"); + goto err_dev; + } + radio->videodev = si470x_viddev_template; + radio->videodev.ctrl_handler = &radio->hdl; + radio->videodev.lock = &radio->lock; + radio->videodev.v4l2_dev = &radio->v4l2_dev; + radio->videodev.release = video_device_release_empty; + set_bit(V4L2_FL_USE_FH_PRIO, &radio->videodev.flags); + video_set_drvdata(&radio->videodev, radio); /* get device and chip versions */ if (si470x_get_all_registers(radio) < 0) { retval = -EIO; - goto err_video; + goto err_ctrl; } dev_info(&intf->dev, "DeviceID=0x%4.4hx ChipID=0x%4.4hx\n", radio->registers[DEVICEID], radio->registers[CHIPID]); @@ -721,7 +664,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, /* get software and hardware versions */ if (si470x_get_scratch_page_versions(radio) < 0) { retval = -EIO; - goto err_video; + goto err_ctrl; } dev_info(&intf->dev, "software version %d, hardware version %d\n", radio->software_version, radio->hardware_version); @@ -764,28 +707,35 @@ static int si470x_usb_driver_probe(struct usb_interface *intf, radio->buffer = kmalloc(radio->buf_size, GFP_KERNEL); if (!radio->buffer) { retval = -EIO; - goto err_video; + goto err_ctrl; } /* rds buffer configuration */ radio->wr_index = 0; radio->rd_index = 0; init_waitqueue_head(&radio->read_queue); + usb_set_intfdata(intf, radio); + + /* start radio */ + retval = si470x_start_usb(radio); + if (retval < 0) + goto err_all; /* register video device */ - retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, + retval = video_register_device(&radio->videodev, VFL_TYPE_RADIO, radio_nr); if (retval) { - dev_warn(&intf->dev, "Could not register video device\n"); + dev_err(&intf->dev, "Could not register video device\n"); goto err_all; } - usb_set_intfdata(intf, radio); return 0; err_all: kfree(radio->buffer); -err_video: - video_device_release(radio->videodev); +err_ctrl: + v4l2_ctrl_handler_free(&radio->hdl); +err_dev: + v4l2_device_unregister(&radio->v4l2_dev); err_urb: usb_free_urb(radio->int_in_urb); err_intbuffer: @@ -803,8 +753,22 @@ err_initial: static int si470x_usb_driver_suspend(struct usb_interface *intf, pm_message_t message) { + struct si470x_device *radio = usb_get_intfdata(intf); + dev_info(&intf->dev, "suspending now...\n"); + /* shutdown interrupt handler */ + if (radio->int_in_running) { + radio->int_in_running = 0; + if (radio->int_in_urb) + usb_kill_urb(radio->int_in_urb); + } + + /* cancel read processes */ + wake_up_interruptible(&radio->read_queue); + + /* stop radio */ + si470x_stop(radio); return 0; } @@ -814,9 +778,12 @@ static int si470x_usb_driver_suspend(struct usb_interface *intf, */ static int si470x_usb_driver_resume(struct usb_interface *intf) { + struct si470x_device *radio = usb_get_intfdata(intf); + dev_info(&intf->dev, "resuming now...\n"); - return 0; + /* start radio */ + return si470x_start_usb(radio); } @@ -828,28 +795,22 @@ static void si470x_usb_driver_disconnect(struct usb_interface *intf) struct si470x_device *radio = usb_get_intfdata(intf); mutex_lock(&radio->lock); - radio->disconnected = 1; + v4l2_device_disconnect(&radio->v4l2_dev); + video_unregister_device(&radio->videodev); usb_set_intfdata(intf, NULL); - if (radio->users == 0) { - /* set led to disconnect state */ - si470x_set_led_state(radio, BLINK_ORANGE_LED); - - /* Free data structures. */ - usb_free_urb(radio->int_in_urb); - - kfree(radio->int_in_buffer); - video_unregister_device(radio->videodev); - kfree(radio->buffer); - mutex_unlock(&radio->lock); - kfree(radio); - } else { - mutex_unlock(&radio->lock); - } + mutex_unlock(&radio->lock); + v4l2_device_put(&radio->v4l2_dev); } /* * si470x_usb_driver - usb driver interface + * + * A note on suspend/resume: this driver had only empty suspend/resume + * functions, and when I tried to test suspend/resume it always disconnected + * instead of resuming (using my ADS InstantFM stick). So I've decided to + * remove these callbacks until someone else with better hardware can + * implement and test this. */ static struct usb_driver si470x_usb_driver = { .name = DRIVER_NAME, @@ -857,8 +818,8 @@ static struct usb_driver si470x_usb_driver = { .disconnect = si470x_usb_driver_disconnect, .suspend = si470x_usb_driver_suspend, .resume = si470x_usb_driver_resume, + .reset_resume = si470x_usb_driver_resume, .id_table = si470x_usb_driver_id_table, - .supports_autosuspend = 1, }; module_usb_driver(si470x_usb_driver); diff --git a/drivers/media/radio/si470x/radio-si470x.h b/drivers/media/radio/si470x/radio-si470x.h index f300a55ed85c..4921cab8e0fa 100644 --- a/drivers/media/radio/si470x/radio-si470x.h +++ b/drivers/media/radio/si470x/radio-si470x.h @@ -36,6 +36,9 @@ #include #include #include +#include +#include +#include #include @@ -141,10 +144,9 @@ * si470x_device - private data */ struct si470x_device { - struct video_device *videodev; - - /* driver management */ - unsigned int users; + struct v4l2_device v4l2_dev; + struct video_device videodev; + struct v4l2_ctrl_handler hdl; /* Silabs internal registers (0..15) */ unsigned short registers[RADIO_REGISTER_NUM]; @@ -174,9 +176,6 @@ struct si470x_device { /* scratch page */ unsigned char software_version; unsigned char hardware_version; - - /* driver management */ - unsigned char disconnected; #endif #if defined(CONFIG_I2C_SI470X) || defined(CONFIG_I2C_SI470X_MODULE) @@ -213,6 +212,7 @@ struct si470x_device { * Common Functions **************************************************************************/ extern struct video_device si470x_viddev_template; +extern const struct v4l2_ctrl_ops si470x_ctrl_ops; int si470x_get_register(struct si470x_device *radio, int regnr); int si470x_set_register(struct si470x_device *radio, int regnr); int si470x_disconnect_check(struct si470x_device *radio); diff --git a/drivers/media/radio/tef6862.c b/drivers/media/radio/tef6862.c index 6418c4c9faf1..06d47e5cce9f 100644 --- a/drivers/media/radio/tef6862.c +++ b/drivers/media/radio/tef6862.c @@ -211,7 +211,7 @@ static struct i2c_driver tef6862_driver = { .name = DRIVER_NAME, }, .probe = tef6862_probe, - .remove = tef6862_remove, + .remove = __devexit_p(tef6862_remove), .id_table = tef6862_id, }; diff --git a/drivers/media/radio/wl128x/fmdrv_v4l2.c b/drivers/media/radio/wl128x/fmdrv_v4l2.c index 077d369a0173..080b96a61f1a 100644 --- a/drivers/media/radio/wl128x/fmdrv_v4l2.c +++ b/drivers/media/radio/wl128x/fmdrv_v4l2.c @@ -518,6 +518,10 @@ int fm_v4l2_init_video_device(struct fmdev *fmdev, int radio_nr) video_set_drvdata(gradio_dev, fmdev); gradio_dev->lock = &fmdev->mutex; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &gradio_dev->flags); /* Register with V4L2 subsystem as RADIO device */ if (video_register_device(gradio_dev, VFL_TYPE_RADIO, radio_nr)) { diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index a3fbb21350e9..f97eeb870455 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -69,6 +69,7 @@ config IR_JVC_DECODER config IR_SONY_DECODER tristate "Enable IR raw decoder for the Sony protocol" depends on RC_CORE + select BITREVERSE default y ---help--- diff --git a/drivers/media/rc/ati_remote.c b/drivers/media/rc/ati_remote.c index baf907b3ce76..7be377fc1be8 100644 --- a/drivers/media/rc/ati_remote.c +++ b/drivers/media/rc/ati_remote.c @@ -1,7 +1,7 @@ /* * USB ATI Remote support * - * Copyright (c) 2011 Anssi Hannula + * Copyright (c) 2011, 2012 Anssi Hannula * Version 2.2.0 Copyright (c) 2004 Torrey Hoffman * Version 2.1.1 Copyright (c) 2002 Vladimir Dergachev * @@ -151,13 +151,57 @@ MODULE_PARM_DESC(mouse, "Enable mouse device, default = yes"); #undef err #define err(format, arg...) printk(KERN_ERR format , ## arg) +struct ati_receiver_type { + /* either default_keymap or get_default_keymap should be set */ + const char *default_keymap; + const char *(*get_default_keymap)(struct usb_interface *interface); +}; + +static const char *get_medion_keymap(struct usb_interface *interface) +{ + struct usb_device *udev = interface_to_usbdev(interface); + + /* + * There are many different Medion remotes shipped with a receiver + * with the same usb id, but the receivers have subtle differences + * in the USB descriptors allowing us to detect them. + */ + + if (udev->manufacturer && udev->product) { + if (udev->actconfig->desc.bmAttributes & USB_CONFIG_ATT_WAKEUP) { + + if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc") + && !strcmp(udev->product, "USB Receiver")) + return RC_MAP_MEDION_X10_DIGITAINER; + + if (!strcmp(udev->manufacturer, "X10 WTI") + && !strcmp(udev->product, "RF receiver")) + return RC_MAP_MEDION_X10_OR2X; + } else { + + if (!strcmp(udev->manufacturer, "X10 Wireless Technology Inc") + && !strcmp(udev->product, "USB Receiver")) + return RC_MAP_MEDION_X10; + } + } + + dev_info(&interface->dev, + "Unknown Medion X10 receiver, using default ati_remote Medion keymap\n"); + + return RC_MAP_MEDION_X10; +} + +static const struct ati_receiver_type type_ati = { .default_keymap = RC_MAP_ATI_X10 }; +static const struct ati_receiver_type type_medion = { .get_default_keymap = get_medion_keymap }; +static const struct ati_receiver_type type_firefly = { .default_keymap = RC_MAP_SNAPSTREAM_FIREFLY }; + static struct usb_device_id ati_remote_table[] = { - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_ATI_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_MEDION_X10 }, - { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)RC_MAP_SNAPSTREAM_FIREFLY }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, LOLA2_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, ATI_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, NVIDIA_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_ati }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, MEDION_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_medion }, + { USB_DEVICE(ATI_REMOTE_VENDOR_ID, FIREFLY_REMOTE_PRODUCT_ID), .driver_info = (unsigned long)&type_firefly }, {} /* Terminating entry */ }; @@ -445,6 +489,7 @@ static void ati_remote_input_report(struct urb *urb) int acc; int remote_num; unsigned char scancode; + u32 wheel_keycode = KEY_RESERVED; int i; /* @@ -484,26 +529,33 @@ static void ati_remote_input_report(struct urb *urb) */ scancode = data[2] & 0x7f; - /* Look up event code index in the mouse translation table. */ - for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { - if (scancode == ati_remote_tbl[i].data) { - index = i; - break; + dbginfo(&ati_remote->interface->dev, + "channel 0x%02x; key data %02x, scancode %02x\n", + remote_num, data[2], scancode); + + if (scancode >= 0x70) { + /* + * This is either a mouse or scrollwheel event, depending on + * the remote/keymap. + * Get the keycode assigned to scancode 0x78/0x70. If it is + * set, assume this is a scrollwheel up/down event. + */ + wheel_keycode = rc_g_keycode_from_table(ati_remote->rdev, + scancode & 0x78); + + if (wheel_keycode == KEY_RESERVED) { + /* scrollwheel was not mapped, assume mouse */ + + /* Look up event code index in the mouse translation table. */ + for (i = 0; ati_remote_tbl[i].kind != KIND_END; i++) { + if (scancode == ati_remote_tbl[i].data) { + index = i; + break; + } + } } } - if (index >= 0) { - dbginfo(&ati_remote->interface->dev, - "channel 0x%02x; mouse data %02x; index %d; keycode %d\n", - remote_num, data[2], index, ati_remote_tbl[index].code); - if (!dev) - return; /* no mouse device */ - } else - dbginfo(&ati_remote->interface->dev, - "channel 0x%02x; key data %02x, scancode %02x\n", - remote_num, data[2], scancode); - - if (index >= 0 && ati_remote_tbl[index].kind == KIND_LITERAL) { input_event(dev, ati_remote_tbl[index].type, ati_remote_tbl[index].code, @@ -542,15 +594,29 @@ static void ati_remote_input_report(struct urb *urb) if (index < 0) { /* Not a mouse event, hand it to rc-core. */ + int count = 1; - /* - * We don't use the rc-core repeat handling yet as - * it would cause ghost repeats which would be a - * regression for this driver. - */ - rc_keydown_notimeout(ati_remote->rdev, scancode, - data[2]); - rc_keyup(ati_remote->rdev); + if (wheel_keycode != KEY_RESERVED) { + /* + * This is a scrollwheel event, send the + * scroll up (0x78) / down (0x70) scancode + * repeatedly as many times as indicated by + * rest of the scancode. + */ + count = (scancode & 0x07) + 1; + scancode &= 0x78; + } + + while (count--) { + /* + * We don't use the rc-core repeat handling yet as + * it would cause ghost repeats which would be a + * regression for this driver. + */ + rc_keydown_notimeout(ati_remote->rdev, scancode, + data[2]); + rc_keyup(ati_remote->rdev); + } return; } @@ -766,6 +832,7 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de struct usb_device *udev = interface_to_usbdev(interface); struct usb_host_interface *iface_host = interface->cur_altsetting; struct usb_endpoint_descriptor *endpoint_in, *endpoint_out; + struct ati_receiver_type *type = (struct ati_receiver_type *)id->driver_info; struct ati_remote *ati_remote; struct input_dev *input_dev; struct rc_dev *rc_dev; @@ -827,10 +894,15 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de snprintf(ati_remote->mouse_name, sizeof(ati_remote->mouse_name), "%s mouse", ati_remote->rc_name); - if (id->driver_info) - rc_dev->map_name = (const char *)id->driver_info; - else - rc_dev->map_name = RC_MAP_ATI_X10; + rc_dev->map_name = RC_MAP_ATI_X10; /* default map */ + + /* set default keymap according to receiver model */ + if (type) { + if (type->default_keymap) + rc_dev->map_name = type->default_keymap; + else if (type->get_default_keymap) + rc_dev->map_name = type->get_default_keymap(interface); + } ati_remote_rc_init(ati_remote); mutex_init(&ati_remote->open_mutex); diff --git a/drivers/media/rc/fintek-cir.c b/drivers/media/rc/fintek-cir.c index 4a3a238bcfbc..6aabf7ae3a31 100644 --- a/drivers/media/rc/fintek-cir.c +++ b/drivers/media/rc/fintek-cir.c @@ -556,11 +556,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id if (request_irq(fintek->cir_irq, fintek_cir_isr, IRQF_SHARED, FINTEK_DRIVER_NAME, (void *)fintek)) - goto failure; + goto failure2; ret = rc_register_device(rdev); if (ret) - goto failure; + goto failure3; device_init_wakeup(&pdev->dev, true); fintek->rdev = rdev; @@ -570,12 +570,11 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id return 0; +failure3: + free_irq(fintek->cir_irq, fintek); +failure2: + release_region(fintek->cir_addr, fintek->cir_port_len); failure: - if (fintek->cir_irq) - free_irq(fintek->cir_irq, fintek); - if (fintek->cir_addr) - release_region(fintek->cir_addr, fintek->cir_port_len); - rc_free_device(rdev); kfree(fintek); diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 7f26fdf2e54e..5dd0386604f0 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -255,7 +255,7 @@ static struct usb_device_id imon_usb_id_table[] = { static struct usb_driver imon_driver = { .name = MOD_NAME, .probe = imon_probe, - .disconnect = imon_disconnect, + .disconnect = __devexit_p(imon_disconnect), .suspend = imon_suspend, .resume = imon_resume, .id_table = imon_usb_id_table, diff --git a/drivers/media/rc/ir-raw.c b/drivers/media/rc/ir-raw.c index 95e630998aaf..a82025121345 100644 --- a/drivers/media/rc/ir-raw.c +++ b/drivers/media/rc/ir-raw.c @@ -46,9 +46,9 @@ static int ir_raw_event_thread(void *data) while (!kthread_should_stop()) { spin_lock_irq(&raw->lock); - retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); + retval = kfifo_len(&raw->kfifo); - if (!retval) { + if (retval < sizeof(ev)) { set_current_state(TASK_INTERRUPTIBLE); if (kthread_should_stop()) @@ -59,11 +59,9 @@ static int ir_raw_event_thread(void *data) continue; } + retval = kfifo_out(&raw->kfifo, &ev, sizeof(ev)); spin_unlock_irq(&raw->lock); - - BUG_ON(retval != sizeof(ev)); - mutex_lock(&ir_raw_handler_lock); list_for_each_entry(handler, &ir_raw_handler_list, list) handler->decode(raw->dev, ev); diff --git a/drivers/media/rc/ir-sanyo-decoder.c b/drivers/media/rc/ir-sanyo-decoder.c index d38fbdd0b25a..7e54ec57bcf9 100644 --- a/drivers/media/rc/ir-sanyo-decoder.c +++ b/drivers/media/rc/ir-sanyo-decoder.c @@ -56,7 +56,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) { struct sanyo_dec *data = &dev->raw->sanyo; u32 scancode; - u8 address, not_address, command, not_command; + u8 address, command, not_command; if (!(dev->raw->enabled_protocols & RC_TYPE_SANYO)) return 0; @@ -154,7 +154,7 @@ static int ir_sanyo_decode(struct rc_dev *dev, struct ir_raw_event ev) break; address = bitrev16((data->bits >> 29) & 0x1fff) >> 3; - not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; + /* not_address = bitrev16((data->bits >> 16) & 0x1fff) >> 3; */ command = bitrev8((data->bits >> 8) & 0xff); not_command = bitrev8((data->bits >> 0) & 0xff); diff --git a/drivers/media/rc/ite-cir.c b/drivers/media/rc/ite-cir.c index 0e49c99abf68..36fe5a349b95 100644 --- a/drivers/media/rc/ite-cir.c +++ b/drivers/media/rc/ite-cir.c @@ -1598,24 +1598,22 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id if (request_irq(itdev->cir_irq, ite_cir_isr, IRQF_SHARED, ITE_DRIVER_NAME, (void *)itdev)) - goto failure; + goto failure2; ret = rc_register_device(rdev); if (ret) - goto failure; + goto failure3; itdev->rdev = rdev; ite_pr(KERN_NOTICE, "driver has been successfully loaded\n"); return 0; +failure3: + free_irq(itdev->cir_irq, itdev); +failure2: + release_region(itdev->cir_addr, itdev->params.io_region_size); failure: - if (itdev->cir_irq) - free_irq(itdev->cir_irq, itdev); - - if (itdev->cir_addr) - release_region(itdev->cir_addr, itdev->params.io_region_size); - rc_free_device(rdev); kfree(itdev); diff --git a/drivers/media/rc/keymaps/Makefile b/drivers/media/rc/keymaps/Makefile index 49ce2662f56b..ab84d66c67c1 100644 --- a/drivers/media/rc/keymaps/Makefile +++ b/drivers/media/rc/keymaps/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-anysee.o \ rc-apac-viewcomp.o \ rc-asus-pc39.o \ + rc-asus-ps3-100.o \ rc-ati-tv-wonder-hd-600.o \ rc-ati-x10.o \ rc-avermedia-a16d.o \ @@ -52,6 +53,8 @@ obj-$(CONFIG_RC_MAP) += rc-adstech-dvb-t-pci.o \ rc-lme2510.o \ rc-manli.o \ rc-medion-x10.o \ + rc-medion-x10-digitainer.o \ + rc-medion-x10-or2x.o \ rc-msi-digivox-ii.o \ rc-msi-digivox-iii.o \ rc-msi-tvanywhere.o \ diff --git a/drivers/media/rc/keymaps/rc-asus-ps3-100.c b/drivers/media/rc/keymaps/rc-asus-ps3-100.c new file mode 100644 index 000000000000..ba76609c5936 --- /dev/null +++ b/drivers/media/rc/keymaps/rc-asus-ps3-100.c @@ -0,0 +1,91 @@ +/* asus-ps3-100.h - Keytable for asus_ps3_100 Remote Controller + * + * Copyright (c) 2012 by Mauro Carvalho Chehab + * + * Based on a previous patch from Remi Schwartz + * + * 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. + */ + +#include +#include + +static struct rc_map_table asus_ps3_100[] = { + { 0x081c, KEY_HOME }, /* home */ + { 0x081e, KEY_TV }, /* tv */ + { 0x0803, KEY_TEXT }, /* teletext */ + { 0x0829, KEY_POWER }, /* close */ + + { 0x080b, KEY_RED }, /* red */ + { 0x080d, KEY_YELLOW }, /* yellow */ + { 0x0806, KEY_BLUE }, /* blue */ + { 0x0807, KEY_GREEN }, /* green */ + + /* Keys 0 to 9 */ + { 0x082a, KEY_0 }, + { 0x0816, KEY_1 }, + { 0x0812, KEY_2 }, + { 0x0814, KEY_3 }, + { 0x0836, KEY_4 }, + { 0x0832, KEY_5 }, + { 0x0834, KEY_6 }, + { 0x080e, KEY_7 }, + { 0x080a, KEY_8 }, + { 0x080c, KEY_9 }, + + { 0x0815, KEY_VOLUMEUP }, + { 0x0826, KEY_VOLUMEDOWN }, + { 0x0835, KEY_CHANNELUP }, /* channel / program + */ + { 0x0824, KEY_CHANNELDOWN }, /* channel / program - */ + + { 0x0808, KEY_UP }, + { 0x0804, KEY_DOWN }, + { 0x0818, KEY_LEFT }, + { 0x0810, KEY_RIGHT }, + { 0x0825, KEY_ENTER }, /* enter */ + + { 0x0822, KEY_EXIT }, /* back */ + { 0x082c, KEY_AB }, /* recall */ + + { 0x0820, KEY_AUDIO }, /* TV audio */ + { 0x0837, KEY_SCREEN }, /* snapshot */ + { 0x082e, KEY_ZOOM }, /* full screen */ + { 0x0802, KEY_MUTE }, /* mute */ + + { 0x0831, KEY_REWIND }, /* backward << */ + { 0x0811, KEY_RECORD }, /* recording */ + { 0x0809, KEY_STOP }, + { 0x0805, KEY_FASTFORWARD }, /* forward >> */ + { 0x0821, KEY_PREVIOUS }, /* rew */ + { 0x081a, KEY_PAUSE }, /* pause */ + { 0x0839, KEY_PLAY }, /* play */ + { 0x0819, KEY_NEXT }, /* forward */ +}; + +static struct rc_map_list asus_ps3_100_map = { +.map = { + .scan = asus_ps3_100, + .size = ARRAY_SIZE(asus_ps3_100), + .rc_type = RC_TYPE_RC5, + .name = RC_MAP_ASUS_PS3_100, +} +}; + +static int __init init_rc_map_asus_ps3_100(void) +{ +return rc_map_register(&asus_ps3_100_map); +} + +static void __exit exit_rc_map_asus_ps3_100(void) +{ +rc_map_unregister(&asus_ps3_100_map); +} + +module_init(init_rc_map_asus_ps3_100) +module_exit(exit_rc_map_asus_ps3_100) + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); diff --git a/drivers/media/rc/keymaps/rc-it913x-v2.c b/drivers/media/rc/keymaps/rc-it913x-v2.c index 28e376e18b99..bd42a30ec06f 100644 --- a/drivers/media/rc/keymaps/rc-it913x-v2.c +++ b/drivers/media/rc/keymaps/rc-it913x-v2.c @@ -40,7 +40,7 @@ static struct rc_map_table it913x_v2_rc[] = { /* Type 2 */ /* keys stereo, snapshot unassigned */ { 0x866b00, KEY_0 }, - { 0x866b1b, KEY_1 }, + { 0x866b01, KEY_1 }, { 0x866b02, KEY_2 }, { 0x866b03, KEY_3 }, { 0x866b04, KEY_4 }, diff --git a/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c new file mode 100644 index 000000000000..966f9b3c71da --- /dev/null +++ b/drivers/media/rc/keymaps/rc-medion-x10-digitainer.c @@ -0,0 +1,123 @@ +/* + * Medion X10 RF remote keytable (Digitainer variant) + * + * Copyright (C) 2012 Anssi Hannula + * + * This keymap is for a variant that has a distinctive scrollwheel instead of + * up/down buttons (tested with P/N 40009936 / 20018268), reportedly + * originally shipped with Medion Digitainer but now sold separately simply as + * an "X10" remote. + * + * 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 + +static struct rc_map_table medion_x10_digitainer[] = { + { 0x02, KEY_POWER }, + + { 0x2c, KEY_TV }, + { 0x2d, KEY_VIDEO }, + { 0x04, KEY_DVD }, /* CD/DVD */ + { 0x16, KEY_TEXT }, /* "teletext" icon, i.e. a screen with lines */ + { 0x06, KEY_AUDIO }, + { 0x2e, KEY_RADIO }, + { 0x31, KEY_EPG }, /* a screen with an open book */ + { 0x05, KEY_IMAGES }, /* Photo */ + { 0x2f, KEY_INFO }, + + { 0x78, KEY_UP }, /* scrollwheel up 1 notch */ + /* 0x79..0x7f: 2-8 notches, driver repeats 0x78 entry */ + + { 0x70, KEY_DOWN }, /* scrollwheel down 1 notch */ + /* 0x71..0x77: 2-8 notches, driver repeats 0x70 entry */ + + { 0x19, KEY_MENU }, + { 0x1d, KEY_LEFT }, + { 0x1e, KEY_OK }, /* scrollwheel press */ + { 0x1f, KEY_RIGHT }, + { 0x20, KEY_BACK }, + + { 0x09, KEY_VOLUMEUP }, + { 0x08, KEY_VOLUMEDOWN }, + { 0x00, KEY_MUTE }, + + { 0x1b, KEY_SELECT }, /* also has "U" rotated 90 degrees CCW */ + + { 0x0b, KEY_CHANNELUP }, + { 0x0c, KEY_CHANNELDOWN }, + { 0x1c, KEY_LAST }, + + { 0x32, KEY_RED }, /* also Audio */ + { 0x33, KEY_GREEN }, /* also Subtitle */ + { 0x34, KEY_YELLOW }, /* also Angle */ + { 0x35, KEY_BLUE }, /* also Title */ + + { 0x28, KEY_STOP }, + { 0x29, KEY_PAUSE }, + { 0x25, KEY_PLAY }, + { 0x21, KEY_PREVIOUS }, + { 0x18, KEY_CAMERA }, + { 0x23, KEY_NEXT }, + { 0x24, KEY_REWIND }, + { 0x27, KEY_RECORD }, + { 0x26, KEY_FORWARD }, + + { 0x0d, KEY_1 }, + { 0x0e, KEY_2 }, + { 0x0f, KEY_3 }, + { 0x10, KEY_4 }, + { 0x11, KEY_5 }, + { 0x12, KEY_6 }, + { 0x13, KEY_7 }, + { 0x14, KEY_8 }, + { 0x15, KEY_9 }, + { 0x17, KEY_0 }, + + /* these do not actually exist on this remote, but these scancodes + * exist on all other Medion X10 remotes and adding them here allows + * such remotes to be adequately usable with this keymap in case + * this keymap is wrongly used with them (which is quite possible as + * there are lots of different Medion X10 remotes): */ + { 0x1a, KEY_UP }, + { 0x22, KEY_DOWN }, +}; + +static struct rc_map_list medion_x10_digitainer_map = { + .map = { + .scan = medion_x10_digitainer, + .size = ARRAY_SIZE(medion_x10_digitainer), + .rc_type = RC_TYPE_OTHER, + .name = RC_MAP_MEDION_X10_DIGITAINER, + } +}; + +static int __init init_rc_map_medion_x10_digitainer(void) +{ + return rc_map_register(&medion_x10_digitainer_map); +} + +static void __exit exit_rc_map_medion_x10_digitainer(void) +{ + rc_map_unregister(&medion_x10_digitainer_map); +} + +module_init(init_rc_map_medion_x10_digitainer) +module_exit(exit_rc_map_medion_x10_digitainer) + +MODULE_DESCRIPTION("Medion X10 RF remote keytable (Digitainer variant)"); +MODULE_AUTHOR("Anssi Hannula "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/rc/keymaps/rc-medion-x10-or2x.c b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c new file mode 100644 index 000000000000..b077300ecb5c --- /dev/null +++ b/drivers/media/rc/keymaps/rc-medion-x10-or2x.c @@ -0,0 +1,108 @@ +/* + * Medion X10 OR22/OR24 RF remote keytable + * + * Copyright (C) 2012 Anssi Hannula + * + * This keymap is for several Medion X10 remotes that have the Windows MCE + * button. This has been tested with a "RF VISTA Remote Control", OR24V, + * P/N 20035335, but should work with other variants that have the same + * buttons, such as OR22V and OR24E. + * + * 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 + +static struct rc_map_table medion_x10_or2x[] = { + { 0x02, KEY_POWER }, + { 0x16, KEY_TEXT }, /* "T" in a box, for teletext */ + + { 0x09, KEY_VOLUMEUP }, + { 0x08, KEY_VOLUMEDOWN }, + { 0x00, KEY_MUTE }, + { 0x0b, KEY_CHANNELUP }, + { 0x0c, KEY_CHANNELDOWN }, + + { 0x32, KEY_RED }, + { 0x33, KEY_GREEN }, + { 0x34, KEY_YELLOW }, + { 0x35, KEY_BLUE }, + + { 0x18, KEY_PVR }, /* record symbol inside a tv symbol */ + { 0x04, KEY_DVD }, /* disc symbol */ + { 0x31, KEY_EPG }, /* a tv schedule symbol */ + { 0x1c, KEY_TV }, /* play symbol inside a tv symbol */ + { 0x20, KEY_BACK }, + { 0x2f, KEY_INFO }, + + { 0x1a, KEY_UP }, + { 0x22, KEY_DOWN }, + { 0x1d, KEY_LEFT }, + { 0x1f, KEY_RIGHT }, + { 0x1e, KEY_OK }, + + { 0x1b, KEY_MEDIA }, /* Windows MCE button */ + + { 0x21, KEY_PREVIOUS }, + { 0x23, KEY_NEXT }, + { 0x24, KEY_REWIND }, + { 0x26, KEY_FORWARD }, + { 0x25, KEY_PLAY }, + { 0x28, KEY_STOP }, + { 0x29, KEY_PAUSE }, + { 0x27, KEY_RECORD }, + + { 0x0d, KEY_1 }, + { 0x0e, KEY_2 }, + { 0x0f, KEY_3 }, + { 0x10, KEY_4 }, + { 0x11, KEY_5 }, + { 0x12, KEY_6 }, + { 0x13, KEY_7 }, + { 0x14, KEY_8 }, + { 0x15, KEY_9 }, + { 0x17, KEY_0 }, + { 0x30, KEY_CLEAR }, + { 0x36, KEY_ENTER }, + { 0x37, KEY_NUMERIC_STAR }, + { 0x38, KEY_NUMERIC_POUND }, +}; + +static struct rc_map_list medion_x10_or2x_map = { + .map = { + .scan = medion_x10_or2x, + .size = ARRAY_SIZE(medion_x10_or2x), + .rc_type = RC_TYPE_OTHER, + .name = RC_MAP_MEDION_X10_OR2X, + } +}; + +static int __init init_rc_map_medion_x10_or2x(void) +{ + return rc_map_register(&medion_x10_or2x_map); +} + +static void __exit exit_rc_map_medion_x10_or2x(void) +{ + rc_map_unregister(&medion_x10_or2x_map); +} + +module_init(init_rc_map_medion_x10_or2x) +module_exit(exit_rc_map_medion_x10_or2x) + +MODULE_DESCRIPTION("Medion X10 OR22/OR24 RF remote keytable"); +MODULE_AUTHOR("Anssi Hannula "); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index e150a2e29a4b..84e06d3aa696 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -520,7 +520,7 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, { char codes[USB_BUFLEN * 3 + 1]; char inout[9]; - u8 cmd, subcmd, data1, data2, data3, data4, data5; + u8 cmd, subcmd, data1, data2, data3, data4; struct device *dev = ir->dev; int i, start, skip = 0; u32 carrier, period; @@ -553,7 +553,6 @@ static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf, data2 = buf[start + 3] & 0xff; data3 = buf[start + 4] & 0xff; data4 = buf[start + 5] & 0xff; - data5 = buf[start + 6] & 0xff; switch (cmd) { case MCE_CMD_NULL: @@ -1443,7 +1442,7 @@ static int mceusb_dev_resume(struct usb_interface *intf) static struct usb_driver mceusb_dev_driver = { .name = DRIVER_NAME, .probe = mceusb_dev_probe, - .disconnect = mceusb_dev_disconnect, + .disconnect = __devexit_p(mceusb_dev_disconnect), .suspend = mceusb_dev_suspend, .resume = mceusb_dev_resume, .reset_resume = mceusb_dev_resume, diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 8b2c071ac0ab..dc8a7dddccd4 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -1075,19 +1075,19 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) if (request_irq(nvt->cir_irq, nvt_cir_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure; + goto failure2; if (!request_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH, NVT_DRIVER_NAME)) - goto failure; + goto failure3; if (request_irq(nvt->cir_wake_irq, nvt_cir_wake_isr, IRQF_SHARED, NVT_DRIVER_NAME, (void *)nvt)) - goto failure; + goto failure4; ret = rc_register_device(rdev); if (ret) - goto failure; + goto failure5; device_init_wakeup(&pdev->dev, true); nvt->rdev = rdev; @@ -1099,17 +1099,15 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) return 0; +failure5: + free_irq(nvt->cir_wake_irq, nvt); +failure4: + release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); +failure3: + free_irq(nvt->cir_irq, nvt); +failure2: + release_region(nvt->cir_addr, CIR_IOREG_LENGTH); failure: - if (nvt->cir_irq) - free_irq(nvt->cir_irq, nvt); - if (nvt->cir_addr) - release_region(nvt->cir_addr, CIR_IOREG_LENGTH); - - if (nvt->cir_wake_irq) - free_irq(nvt->cir_wake_irq, nvt); - if (nvt->cir_wake_addr) - release_region(nvt->cir_wake_addr, CIR_IOREG_LENGTH); - rc_free_device(rdev); kfree(nvt); diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index efc6a514348a..fae1615e0ff2 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -221,7 +221,6 @@ static int __init loop_init(void) rc->s_idle = loop_set_idle; rc->s_learning_mode = loop_set_learning_mode; rc->s_carrier_report = loop_set_carrier_report; - rc->priv = &loopdev; loopdev.txmask = RXMASK_REGULAR; loopdev.txcarrier = 36000; diff --git a/drivers/media/rc/redrat3.c b/drivers/media/rc/redrat3.c index ad95c67a4dba..2878b0ed9741 100644 --- a/drivers/media/rc/redrat3.c +++ b/drivers/media/rc/redrat3.c @@ -1277,7 +1277,7 @@ static int redrat3_dev_resume(struct usb_interface *intf) static struct usb_driver redrat3_dev_driver = { .name = DRIVER_NAME, .probe = redrat3_dev_probe, - .disconnect = redrat3_dev_disconnect, + .disconnect = __devexit_p(redrat3_dev_disconnect), .suspend = redrat3_dev_suspend, .resume = redrat3_dev_resume, .reset_resume = redrat3_dev_resume, diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index ce1e7ba940f6..99937c94d7df 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -472,6 +472,9 @@ comment "Camera sensor devices" config VIDEO_APTINA_PLL tristate +config VIDEO_SMIAPP_PLL + tristate + config VIDEO_OV7670 tristate "OmniVision OV7670 sensor support" depends on I2C && VIDEO_V4L2 @@ -556,6 +559,8 @@ config VIDEO_S5K6AA This is a V4L2 sensor-level driver for Samsung S5K6AA(FX) 1.3M camera sensor with an embedded SoC image signal processor. +source "drivers/media/video/smiapp/Kconfig" + comment "Flash devices" config VIDEO_ADP1653 @@ -644,6 +649,8 @@ menuconfig V4L_USB_DRIVERS if V4L_USB_DRIVERS +source "drivers/media/video/au0828/Kconfig" + source "drivers/media/video/uvc/Kconfig" source "drivers/media/video/gspca/Kconfig" @@ -662,8 +669,6 @@ source "drivers/media/video/tm6000/Kconfig" source "drivers/media/video/usbvision/Kconfig" -source "drivers/media/video/et61x251/Kconfig" - source "drivers/media/video/sn9c102/Kconfig" source "drivers/media/video/pwc/Kconfig" @@ -721,8 +726,6 @@ menuconfig V4L_PCI_DRIVERS if V4L_PCI_DRIVERS -source "drivers/media/video/au0828/Kconfig" - source "drivers/media/video/bt8xx/Kconfig" source "drivers/media/video/cx18/Kconfig" @@ -794,6 +797,19 @@ source "drivers/media/video/saa7164/Kconfig" source "drivers/media/video/zoran/Kconfig" +config STA2X11_VIP + tristate "STA2X11 VIP Video For Linux" + depends on STA2X11 + select VIDEO_ADV7180 if VIDEO_HELPER_CHIPS_AUTO + select VIDEOBUF_DMA_CONTIG + depends on PCI && VIDEO_V4L2 && VIRT_TO_BUS + help + Say Y for support for STA2X11 VIP (Video Input Port) capture + device. + + To compile this driver as a module, choose M here: the + module will be called sta2x11_vip. + endif # V4L_PCI_DRIVERS # @@ -1127,19 +1143,6 @@ config VIDEO_MX2 This is a v4l2 driver for the i.MX27 and the i.MX25 Camera Sensor Interface -config VIDEO_SAMSUNG_S5P_FIMC - tristate "Samsung S5P and EXYNOS4 camera interface driver (EXPERIMENTAL)" - depends on VIDEO_V4L2 && I2C && PLAT_S5P && PM_RUNTIME && \ - VIDEO_V4L2_SUBDEV_API && EXPERIMENTAL - select VIDEOBUF2_DMA_CONTIG - select V4L2_MEM2MEM_DEV - ---help--- - This is a v4l2 driver for Samsung S5P and EXYNOS4 camera - host interface and video postprocessor. - - To compile this driver as a module, choose M here: the - module will be called s5p-fimc. - config VIDEO_ATMEL_ISI tristate "ATMEL Image Sensor Interface (ISI) support" depends on VIDEO_DEV && SOC_CAMERA && ARCH_AT91 @@ -1148,16 +1151,7 @@ config VIDEO_ATMEL_ISI This module makes the ATMEL Image Sensor Interface available as a v4l2 device. -config VIDEO_S5P_MIPI_CSIS - tristate "Samsung S5P and EXYNOS4 MIPI CSI receiver driver" - depends on VIDEO_V4L2 && PM_RUNTIME && PLAT_S5P - depends on VIDEO_V4L2_SUBDEV_API && REGULATOR - ---help--- - This is a v4l2 driver for Samsung S5P/EXYNOS4 MIPI-CSI receiver. - - To compile this driver as a module, choose M here: the - module will be called s5p-csis. - +source "drivers/media/video/s5p-fimc/Kconfig" source "drivers/media/video/s5p-tv/Kconfig" endif # V4L_PLATFORM_DRIVERS diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a6282a3a6a82..d209de0e0ca8 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -79,9 +79,12 @@ obj-$(CONFIG_VIDEO_SR030PC30) += sr030pc30.o obj-$(CONFIG_VIDEO_NOON010PC30) += noon010pc30.o obj-$(CONFIG_VIDEO_M5MOLS) += m5mols/ obj-$(CONFIG_VIDEO_S5K6AA) += s5k6aa.o +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp/ obj-$(CONFIG_VIDEO_ADP1653) += adp1653.o obj-$(CONFIG_VIDEO_AS3645A) += as3645a.o +obj-$(CONFIG_VIDEO_SMIAPP_PLL) += smiapp-pll.o + obj-$(CONFIG_SOC_CAMERA_IMX074) += imx074.o obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o @@ -120,6 +123,7 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o +obj-$(CONFIG_STA2X11_VIP) += sta2x11_vip.o obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o @@ -152,7 +156,6 @@ obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o obj-$(CONFIG_USB_SN9C102) += sn9c102/ -obj-$(CONFIG_USB_ET61X251) += et61x251/ obj-$(CONFIG_USB_PWC) += pwc/ obj-$(CONFIG_USB_GSPCA) += gspca/ diff --git a/drivers/media/video/adp1653.c b/drivers/media/video/adp1653.c index 5b045b4a66fe..57e87090388d 100644 --- a/drivers/media/video/adp1653.c +++ b/drivers/media/video/adp1653.c @@ -34,7 +34,6 @@ #include #include #include -#include #include #include @@ -282,19 +281,19 @@ adp1653_init_device(struct adp1653_flash *flash) return -EIO; } - mutex_lock(&flash->ctrls.lock); + mutex_lock(flash->ctrls.lock); /* Reset faults before reading new ones. */ flash->fault = 0; rval = adp1653_get_fault(flash); - mutex_unlock(&flash->ctrls.lock); + mutex_unlock(flash->ctrls.lock); if (rval > 0) { dev_err(&client->dev, "faults detected: 0x%1.1x\n", rval); return -EIO; } - mutex_lock(&flash->ctrls.lock); + mutex_lock(flash->ctrls.lock); rval = adp1653_update_hw(flash); - mutex_unlock(&flash->ctrls.lock); + mutex_unlock(flash->ctrls.lock); if (rval) { dev_err(&client->dev, "adp1653_update_hw failed at %s\n", __func__); diff --git a/drivers/media/video/adv7180.c b/drivers/media/video/adv7180.c index b8b6c4b0cad4..174bffacf117 100644 --- a/drivers/media/video/adv7180.c +++ b/drivers/media/video/adv7180.c @@ -48,6 +48,7 @@ #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_INPUT_CONTROL_INSEL_MASK 0x0f #define ADV7180_EXTENDED_OUTPUT_CONTROL_REG 0x04 #define ADV7180_EXTENDED_OUTPUT_CONTROL_NTSCDIS 0xC5 @@ -55,9 +56,29 @@ #define ADV7180_AUTODETECT_ENABLE_REG 0x07 #define ADV7180_AUTODETECT_DEFAULT 0x7f +#define ADV7180_CON_REG 0x08 /*Unsigned */ +#define CON_REG_MIN 0 +#define CON_REG_DEF 128 +#define CON_REG_MAX 255 + +#define ADV7180_BRI_REG 0x0a /*Signed */ +#define BRI_REG_MIN -128 +#define BRI_REG_DEF 0 +#define BRI_REG_MAX 127 + +#define ADV7180_HUE_REG 0x0b /*Signed, inverted */ +#define HUE_REG_MIN -127 +#define HUE_REG_DEF 0 +#define HUE_REG_MAX 128 + #define ADV7180_ADI_CTRL_REG 0x0e #define ADV7180_ADI_CTRL_IRQ_SPACE 0x20 +#define ADV7180_PWR_MAN_REG 0x0f +#define ADV7180_PWR_MAN_ON 0x04 +#define ADV7180_PWR_MAN_OFF 0x24 +#define ADV7180_PWR_MAN_RES 0x80 + #define ADV7180_STATUS1_REG 0x10 #define ADV7180_STATUS1_IN_LOCK 0x01 #define ADV7180_STATUS1_AUTOD_MASK 0x70 @@ -78,6 +99,12 @@ #define ADV7180_ICONF1_PSYNC_ONLY 0x10 #define ADV7180_ICONF1_ACTIVE_TO_CLR 0xC0 +#define ADV7180_SD_SAT_CB_REG 0xe3 /*Unsigned */ +#define ADV7180_SD_SAT_CR_REG 0xe4 /*Unsigned */ +#define SAT_REG_MIN 0 +#define SAT_REG_DEF 128 +#define SAT_REG_MAX 255 + #define ADV7180_IRQ1_LOCK 0x01 #define ADV7180_IRQ1_UNLOCK 0x02 #define ADV7180_ISR1_ADI 0x42 @@ -90,6 +117,9 @@ #define ADV7180_IMR3_ADI 0x4C #define ADV7180_IMR4_ADI 0x50 +#define ADV7180_NTSC_V_BIT_END_REG 0xE6 +#define ADV7180_NTSC_V_BIT_END_MANUAL_NVEND 0x4F + struct adv7180_state { struct v4l2_subdev sd; struct work_struct work; @@ -97,6 +127,11 @@ struct adv7180_state { int irq; v4l2_std_id curr_norm; bool autodetect; + s8 brightness; + s16 hue; + u8 contrast; + u8 saturation; + u8 input; }; static v4l2_std_id adv7180_std_to_v4l2(u8 status1) @@ -155,7 +190,7 @@ static u32 adv7180_status_to_v4l2(u8 status1) } static int __adv7180_status(struct i2c_client *client, u32 *status, - v4l2_std_id *std) + v4l2_std_id *std) { int status1 = i2c_smbus_read_byte_data(client, ADV7180_STATUS1_REG); @@ -192,6 +227,36 @@ static int adv7180_querystd(struct v4l2_subdev *sd, v4l2_std_id *std) return err; } +static int adv7180_s_routing(struct v4l2_subdev *sd, u32 input, + u32 output, u32 config) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (ret) + return ret; + + /*We cannot discriminate between LQFP and 40-pin LFCSP, so accept + * all inputs and let the card driver take care of validation + */ + if ((input & ADV7180_INPUT_CONTROL_INSEL_MASK) != input) + goto out; + + ret = i2c_smbus_read_byte_data(client, ADV7180_INPUT_CONTROL_REG); + + if (ret < 0) + goto out; + + ret &= ~ADV7180_INPUT_CONTROL_INSEL_MASK; + ret = i2c_smbus_write_byte_data(client, + ADV7180_INPUT_CONTROL_REG, ret | input); + state->input = input; +out: + mutex_unlock(&state->mutex); + return ret; +} + static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) { struct adv7180_state *state = to_state(sd); @@ -205,7 +270,7 @@ static int adv7180_g_input_status(struct v4l2_subdev *sd, u32 *status) } static int adv7180_g_chip_ident(struct v4l2_subdev *sd, - struct v4l2_dbg_chip_ident *chip) + struct v4l2_dbg_chip_ident *chip) { struct i2c_client *client = v4l2_get_subdevdata(sd); @@ -222,9 +287,10 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) /* 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); + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); if (ret < 0) goto out; @@ -236,7 +302,8 @@ static int adv7180_s_std(struct v4l2_subdev *sd, v4l2_std_id std) goto out; ret = i2c_smbus_write_byte_data(client, - ADV7180_INPUT_CONTROL_REG, ret); + ADV7180_INPUT_CONTROL_REG, + ret | state->input); if (ret < 0) goto out; @@ -249,14 +316,138 @@ out: return ret; } +static int adv7180_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) +{ + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(qc, BRI_REG_MIN, BRI_REG_MAX, + 1, BRI_REG_DEF); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, HUE_REG_MIN, HUE_REG_MAX, + 1, HUE_REG_DEF); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(qc, CON_REG_MIN, CON_REG_MAX, + 1, CON_REG_DEF); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(qc, SAT_REG_MIN, SAT_REG_MAX, + 1, SAT_REG_DEF); + default: + break; + } + + return -EINVAL; +} + +static int adv7180_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct adv7180_state *state = to_state(sd); + int ret = mutex_lock_interruptible(&state->mutex); + if (ret) + return ret; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->brightness; + break; + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = state->saturation; + break; + default: + ret = -EINVAL; + } + + mutex_unlock(&state->mutex); + return ret; +} + +static int adv7180_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + 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; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if ((ctrl->value > BRI_REG_MAX) + || (ctrl->value < BRI_REG_MIN)) { + ret = -ERANGE; + break; + } + state->brightness = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_BRI_REG, + state->brightness); + break; + case V4L2_CID_HUE: + if ((ctrl->value > HUE_REG_MAX) + || (ctrl->value < HUE_REG_MIN)) { + ret = -ERANGE; + break; + } + state->hue = ctrl->value; + /*Hue is inverted according to HSL chart */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_HUE_REG, -state->hue); + break; + case V4L2_CID_CONTRAST: + if ((ctrl->value > CON_REG_MAX) + || (ctrl->value < CON_REG_MIN)) { + ret = -ERANGE; + break; + } + state->contrast = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_CON_REG, + state->contrast); + break; + case V4L2_CID_SATURATION: + if ((ctrl->value > SAT_REG_MAX) + || (ctrl->value < SAT_REG_MIN)) { + ret = -ERANGE; + break; + } + /* + *This could be V4L2_CID_BLUE_BALANCE/V4L2_CID_RED_BALANCE + *Let's not confuse the user, everybody understands saturation + */ + state->saturation = ctrl->value; + ret = i2c_smbus_write_byte_data(client, + ADV7180_SD_SAT_CB_REG, + state->saturation); + if (ret < 0) + break; + ret = i2c_smbus_write_byte_data(client, + ADV7180_SD_SAT_CR_REG, + state->saturation); + break; + default: + ret = -EINVAL; + } + + 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, + .s_routing = adv7180_s_routing, }; static const struct v4l2_subdev_core_ops adv7180_core_ops = { .g_chip_ident = adv7180_g_chip_ident, .s_std = adv7180_s_std, + .queryctrl = adv7180_queryctrl, + .g_ctrl = adv7180_g_ctrl, + .s_ctrl = adv7180_s_ctrl, }; static const struct v4l2_subdev_ops adv7180_ops = { @@ -267,13 +458,13 @@ static const struct v4l2_subdev_ops adv7180_ops = { static void adv7180_work(struct work_struct *work) { struct adv7180_state *state = container_of(work, struct adv7180_state, - work); + 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); + 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); @@ -297,13 +488,128 @@ static irqreturn_t adv7180_irq(int irq, void *devid) return IRQ_HANDLED; } -/* - * Generic i2c probe - * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1' - */ +static int init_device(struct i2c_client *client, struct adv7180_state *state) +{ + int ret; + + /* Initialize adv7180 */ + /* Enable autodetection */ + if (state->autodetect) { + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM + | state->input); + if (ret < 0) + return ret; + + ret = + i2c_smbus_write_byte_data(client, + ADV7180_AUTODETECT_ENABLE_REG, + ADV7180_AUTODETECT_DEFAULT); + if (ret < 0) + return ret; + } else { + ret = v4l2_std_to_adv7180(state->curr_norm); + if (ret < 0) + return ret; + + ret = + i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, + ret | state->input); + if (ret < 0) + return ret; + + } + /* 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) + return ret; + + /* Manually set V bit end position in NTSC mode */ + ret = i2c_smbus_write_byte_data(client, + ADV7180_NTSC_V_BIT_END_REG, + ADV7180_NTSC_V_BIT_END_MANUAL_NVEND); + if (ret < 0) + return ret; + + /* 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) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + ADV7180_ADI_CTRL_IRQ_SPACE); + if (ret < 0) + return ret; + + /* 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) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR1_ADI, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR2_ADI, 0); + if (ret < 0) + return ret; + + /* enable AD change interrupts interrupts */ + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR3_ADI, + ADV7180_IRQ3_AD_CHANGE); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_IMR4_ADI, 0); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_ADI_CTRL_REG, + 0); + if (ret < 0) + return ret; + } + + /*Set default value for controls */ + ret = i2c_smbus_write_byte_data(client, ADV7180_BRI_REG, + state->brightness); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_HUE_REG, state->hue); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_CON_REG, + state->contrast); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CB_REG, + state->saturation); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_SD_SAT_CR_REG, + state->saturation); + if (ret < 0) + return ret; + + return 0; +} static __devinit int adv7180_probe(struct i2c_client *client, - const struct i2c_device_id *id) + const struct i2c_device_id *id) { struct adv7180_state *state; struct v4l2_subdev *sd; @@ -314,7 +620,7 @@ static __devinit int adv7180_probe(struct i2c_client *client, return -EIO; v4l_info(client, "chip found @ 0x%02x (%s)\n", - client->addr << 1, client->adapter->name); + client->addr, client->adapter->name); state = kzalloc(sizeof(struct adv7180_state), GFP_KERNEL); if (state == NULL) { @@ -326,73 +632,17 @@ static __devinit int adv7180_probe(struct i2c_client *client, INIT_WORK(&state->work, adv7180_work); mutex_init(&state->mutex); state->autodetect = true; + state->brightness = BRI_REG_DEF; + state->hue = HUE_REG_DEF; + state->contrast = CON_REG_DEF; + state->saturation = SAT_REG_DEF; + state->input = 0; sd = &state->sd; v4l2_i2c_subdev_init(sd, client, &adv7180_ops); - /* Initialize adv7180 */ - /* Enable autodetection */ - ret = i2c_smbus_write_byte_data(client, ADV7180_INPUT_CONTROL_REG, - ADV7180_INPUT_CONTROL_AD_PAL_BG_NTSC_J_SECAM); - if (ret < 0) + ret = init_device(client, state); + if (0 != ret) 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: @@ -432,16 +682,49 @@ static const struct i2c_device_id adv7180_id[] = { {}, }; +#ifdef CONFIG_PM +static int adv7180_suspend(struct i2c_client *client, pm_message_t state) +{ + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_OFF); + if (ret < 0) + return ret; + return 0; +} + +static int adv7180_resume(struct i2c_client *client) +{ + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct adv7180_state *state = to_state(sd); + int ret; + + ret = i2c_smbus_write_byte_data(client, ADV7180_PWR_MAN_REG, + ADV7180_PWR_MAN_ON); + if (ret < 0) + return ret; + ret = init_device(client, state); + if (ret < 0) + return ret; + return 0; +} +#endif + MODULE_DEVICE_TABLE(i2c, adv7180_id); static struct i2c_driver adv7180_driver = { .driver = { - .owner = THIS_MODULE, - .name = DRIVER_NAME, - }, - .probe = adv7180_probe, - .remove = __devexit_p(adv7180_remove), - .id_table = adv7180_id, + .owner = THIS_MODULE, + .name = DRIVER_NAME, + }, + .probe = adv7180_probe, + .remove = __devexit_p(adv7180_remove), +#ifdef CONFIG_PM + .suspend = adv7180_suspend, + .resume = adv7180_resume, +#endif + .id_table = adv7180_id, }; module_i2c_driver(adv7180_driver); diff --git a/drivers/media/video/adv7343.c b/drivers/media/video/adv7343.c index 119b60401bf3..2b5aa676a84e 100644 --- a/drivers/media/video/adv7343.c +++ b/drivers/media/video/adv7343.c @@ -130,14 +130,12 @@ static int adv7343_setstd(struct v4l2_subdev *sd, v4l2_std_id std) { struct adv7343_state *state = to_state(sd); struct adv7343_std_info *std_info; - int output_idx, num_std; + int num_std; char *fsc_ptr; u8 reg, val; int err = 0; int i = 0; - output_idx = state->output; - std_info = (struct adv7343_std_info *)stdinfo; num_std = ARRAY_SIZE(stdinfo); diff --git a/drivers/media/video/aptina-pll.c b/drivers/media/video/aptina-pll.c index 0bd3813bb59d..8153a449846b 100644 --- a/drivers/media/video/aptina-pll.c +++ b/drivers/media/video/aptina-pll.c @@ -148,9 +148,8 @@ int aptina_pll_calculate(struct device *dev, unsigned int mf_high; unsigned int mf_low; - mf_low = max(roundup(mf_min, mf_inc), - DIV_ROUND_UP(pll->ext_clock * p1, - limits->int_clock_max * div)); + mf_low = roundup(max(mf_min, DIV_ROUND_UP(pll->ext_clock * p1, + limits->int_clock_max * div)), mf_inc); mf_high = min(mf_max, pll->ext_clock * p1 / (limits->int_clock_min * div)); diff --git a/drivers/media/video/arv.c b/drivers/media/video/arv.c index b6ed44aebe30..e346d32d08ce 100644 --- a/drivers/media/video/arv.c +++ b/drivers/media/video/arv.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -403,7 +404,8 @@ static int ar_querycap(struct file *file, void *priv, strlcpy(vcap->driver, ar->vdev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "Colour AR VGA", sizeof(vcap->card)); strlcpy(vcap->bus_info, "Platform", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -709,6 +711,8 @@ static int ar_initialize(struct ar *ar) static const struct v4l2_file_operations ar_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, .read = ar_read, .unlocked_ioctl = video_ioctl2, }; @@ -769,6 +773,7 @@ static int __init ar_init(void) ar->vdev.fops = &ar_fops; ar->vdev.ioctl_ops = &ar_ioctl_ops; ar->vdev.release = video_device_release_empty; + set_bit(V4L2_FL_USE_FH_PRIO, &ar->vdev.flags); video_set_drvdata(&ar->vdev, ar); if (vga) { diff --git a/drivers/media/video/as3645a.c b/drivers/media/video/as3645a.c index 7a3371f044fc..c4b03572dce8 100644 --- a/drivers/media/video/as3645a.c +++ b/drivers/media/video/as3645a.c @@ -713,7 +713,7 @@ static int as3645a_resume(struct device *dev) * The number of LEDs reported in platform data is used to compute default * limits. Parameters passed through platform data can override those limits. */ -static int as3645a_init_controls(struct as3645a *flash) +static int __devinit as3645a_init_controls(struct as3645a *flash) { const struct as3645a_platform_data *pdata = flash->pdata; struct v4l2_ctrl *ctrl; @@ -804,8 +804,8 @@ static int as3645a_init_controls(struct as3645a *flash) return flash->ctrls.error; } -static int as3645a_probe(struct i2c_client *client, - const struct i2c_device_id *devid) +static int __devinit as3645a_probe(struct i2c_client *client, + const struct i2c_device_id *devid) { struct as3645a *flash; int ret; @@ -846,7 +846,7 @@ done: return ret; } -static int __exit as3645a_remove(struct i2c_client *client) +static int __devexit as3645a_remove(struct i2c_client *client) { struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct as3645a *flash = to_as3645a(subdev); @@ -877,7 +877,7 @@ static struct i2c_driver as3645a_i2c_driver = { .pm = &as3645a_pm_ops, }, .probe = as3645a_probe, - .remove = __exit_p(as3645a_remove), + .remove = __devexit_p(as3645a_remove), .id_table = as3645a_id_table, }; diff --git a/drivers/media/video/atmel-isi.c b/drivers/media/video/atmel-isi.c index ec3f6a06f9c3..6274a91c25c7 100644 --- a/drivers/media/video/atmel-isi.c +++ b/drivers/media/video/atmel-isi.c @@ -260,7 +260,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct atmel_isi *isi = ici->priv; unsigned long size; - int ret, bytes_per_line; + int ret; /* Reset ISI */ ret = atmel_isi_wait_status(isi, WAIT_ISI_RESET); @@ -271,13 +271,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, /* Disable all interrupts */ isi_writel(isi, ISI_INTDIS, ~0UL); - bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; - - size = bytes_per_line * icd->user_height; + size = icd->sizeimage; if (!*nbuffers || *nbuffers > MAX_BUFFER_NUM) *nbuffers = MAX_BUFFER_NUM; @@ -316,13 +310,8 @@ static int buffer_prepare(struct vb2_buffer *vb) struct atmel_isi *isi = ici->priv; unsigned long size; struct isi_dma_desc *desc; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytes_per_line < 0) - return bytes_per_line; - - size = bytes_per_line * icd->user_height; + size = icd->sizeimage; if (vb2_plane_size(vb, 0) < size) { dev_err(icd->parent, "%s data will not fit into plane (%lu < %lu)\n", @@ -638,6 +627,7 @@ static const struct soc_mbus_pixelfmt isi_camera_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }; diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig index 81ba9d9d1b52..23f7fd22f0eb 100644 --- a/drivers/media/video/au0828/Kconfig +++ b/drivers/media/video/au0828/Kconfig @@ -6,7 +6,8 @@ config VIDEO_AU0828 select I2C_ALGOBIT select VIDEO_TVEEPROM select VIDEOBUF_VMALLOC - select DVB_AU8522 if !DVB_FE_CUSTOMISE + select DVB_AU8522_DTV if !DVB_FE_CUSTOMISE + select DVB_AU8522_V4L if !DVB_FE_CUSTOMISE select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c index 1c6015a04f96..e3fe9a6637f6 100644 --- a/drivers/media/video/au0828/au0828-cards.c +++ b/drivers/media/video/au0828/au0828-cards.c @@ -325,6 +325,8 @@ struct usb_device_id au0828_usb_id_table[] = { .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, { USB_DEVICE(0x2040, 0x7281), .driver_info = AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL }, + { USB_DEVICE(0x05e1, 0x0480), + .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, { USB_DEVICE(0x2040, 0x8200), .driver_info = AU0828_BOARD_HAUPPAUGE_WOODBURY }, { USB_DEVICE(0x2040, 0x7260), diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c index 518216743c9c..39ece8e24985 100644 --- a/drivers/media/video/au0828/au0828-dvb.c +++ b/drivers/media/video/au0828/au0828-dvb.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "au0828.h" #include "au8522.h" @@ -79,9 +80,16 @@ static struct au8522_config hauppauge_woodbury_config = { .vsb_if = AU8522_IF_3_25MHZ, }; -static struct xc5000_config hauppauge_hvr950q_tunerconfig = { +static struct xc5000_config hauppauge_xc5000a_config = { .i2c_address = 0x61, .if_khz = 6000, + .chip_id = XC5000A, +}; + +static struct xc5000_config hauppauge_xc5000c_config = { + .i2c_address = 0x61, + .if_khz = 6000, + .chip_id = XC5000C, }; static struct mxl5007t_config mxl5007t_hvr950q_config = { @@ -383,8 +391,19 @@ int au0828_dvb_register(struct au0828_dev *dev) &hauppauge_hvr950q_config, &dev->i2c_adap); if (dvb->frontend != NULL) - dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig); + switch (dev->board.tuner_type) { + default: + case TUNER_XC5000: + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000a_config); + break; + case TUNER_XC5000C: + dvb_attach(xc5000_attach, dvb->frontend, + &dev->i2c_adap, + &hauppauge_xc5000c_config); + break; + } break; case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL: dvb->frontend = dvb_attach(au8522_attach, @@ -411,7 +430,7 @@ int au0828_dvb_register(struct au0828_dev *dev) if (dvb->frontend != NULL) { dvb_attach(xc5000_attach, dvb->frontend, &dev->i2c_adap, - &hauppauge_hvr950q_tunerconfig); + &hauppauge_xc5000a_config); } break; default: diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c index 0b3e481ffe8c..ac3dd733ab81 100644 --- a/drivers/media/video/au0828/au0828-video.c +++ b/drivers/media/video/au0828/au0828-video.c @@ -120,7 +120,7 @@ static void au0828_irq_callback(struct urb *urb) struct au0828_dmaqueue *dma_q = urb->context; struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq); unsigned long flags = 0; - int rc, i; + int i; switch (urb->status) { case 0: /* success */ @@ -138,7 +138,7 @@ static void au0828_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock_irqsave(&dev->slock, flags); - rc = dev->isoc_ctl.isoc_copy(dev, urb); + dev->isoc_ctl.isoc_copy(dev, urb); spin_unlock_irqrestore(&dev->slock, flags); /* Reset urb buffers */ @@ -1881,7 +1881,7 @@ int au0828_analog_register(struct au0828_dev *dev, int retval = -ENOMEM; struct usb_host_interface *iface_desc; struct usb_endpoint_descriptor *endpoint; - int i; + int i, ret; dprintk(1, "au0828_analog_register called!\n"); @@ -1951,8 +1951,8 @@ int au0828_analog_register(struct au0828_dev *dev, dev->vbi_dev = video_device_alloc(); if (NULL == dev->vbi_dev) { dprintk(1, "Can't allocate vbi_device.\n"); - kfree(dev->vdev); - return -ENOMEM; + ret = -ENOMEM; + goto err_vdev; } /* Fill the video capture device struct */ @@ -1971,8 +1971,8 @@ int au0828_analog_register(struct au0828_dev *dev, if (retval != 0) { dprintk(1, "unable to register video device (error = %d).\n", retval); - video_device_release(dev->vdev); - return -ENODEV; + ret = -ENODEV; + goto err_vbi_dev; } /* Register the vbi device */ @@ -1981,13 +1981,18 @@ int au0828_analog_register(struct au0828_dev *dev, if (retval != 0) { dprintk(1, "unable to register vbi device (error = %d).\n", retval); - video_device_release(dev->vbi_dev); - video_device_release(dev->vdev); - return -ENODEV; + ret = -ENODEV; + goto err_vbi_dev; } dprintk(1, "%s completed!\n", __func__); return 0; + +err_vbi_dev: + video_device_release(dev->vbi_dev); +err_vdev: + video_device_release(dev->vdev); + return ret; } diff --git a/drivers/media/video/blackfin/bfin_capture.c b/drivers/media/video/blackfin/bfin_capture.c index 514fcf742f5a..0aba45e34f70 100644 --- a/drivers/media/video/blackfin/bfin_capture.c +++ b/drivers/media/video/blackfin/bfin_capture.c @@ -942,6 +942,10 @@ static int __devinit bcap_probe(struct platform_device *pdev) INIT_LIST_HEAD(&bcap_dev->dma_queue); vfd->lock = &bcap_dev->mutex; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); /* register video device */ ret = video_register_device(bcap_dev->video_dev, VFL_TYPE_GRABBER, -1); diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c index e581b37be789..a9cfb0f4be48 100644 --- a/drivers/media/video/bt8xx/bttv-driver.c +++ b/drivers/media/video/bt8xx/bttv-driver.c @@ -663,7 +663,7 @@ static const struct v4l2_queryctrl bttv_ctls[] = { .minimum = 0, .maximum = 65535, .step = 128, - .default_value = 32768, + .default_value = 27648, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_SATURATION, @@ -4394,7 +4394,7 @@ static int __devinit bttv_probe(struct pci_dev *dev, if (!bttv_tvcards[btv->c.type].no_video) { bttv_register_video(btv); bt848_bright(btv,32768); - bt848_contrast(btv,32768); + bt848_contrast(btv, 27648); bt848_hue(btv,32768); bt848_sat(btv,32768); audio_mute(btv, 1); diff --git a/drivers/media/video/bw-qcam.c b/drivers/media/video/bw-qcam.c index f09df9dffaae..2520219f01ba 100644 --- a/drivers/media/video/bw-qcam.c +++ b/drivers/media/video/bw-qcam.c @@ -77,6 +77,9 @@ OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include +#include /* One from column A... */ #define QC_NOTSET 0 @@ -103,6 +106,7 @@ OTHER DEALINGS IN THE SOFTWARE. struct qcam { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler hdl; struct pardevice *pdev; struct parport *pport; struct mutex lock; @@ -646,7 +650,8 @@ static int qcam_querycap(struct file *file, void *priv, strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "B&W Quickcam", sizeof(vcap->card)); strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -674,72 +679,6 @@ static int qcam_s_input(struct file *file, void *fh, unsigned int inp) return (inp > 0) ? -EINVAL : 0; } -static int qcam_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, 180); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192); - case V4L2_CID_GAMMA: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 105); - } - return -EINVAL; -} - -static int qcam_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct qcam *qcam = video_drvdata(file); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = qcam->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = qcam->contrast; - break; - case V4L2_CID_GAMMA: - ctrl->value = qcam->whitebal; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int qcam_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct qcam *qcam = video_drvdata(file); - int ret = 0; - - mutex_lock(&qcam->lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - qcam->brightness = ctrl->value; - break; - case V4L2_CID_CONTRAST: - qcam->contrast = ctrl->value; - break; - case V4L2_CID_GAMMA: - qcam->whitebal = ctrl->value; - break; - default: - ret = -EINVAL; - break; - } - if (ret == 0) { - qc_setscanmode(qcam); - qcam->status |= QC_PARAM_CHANGE; - } - mutex_unlock(&qcam->lock); - return ret; -} - static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct qcam *qcam = video_drvdata(file); @@ -856,8 +795,40 @@ static ssize_t qcam_read(struct file *file, char __user *buf, return len; } +static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct qcam *qcam = + container_of(ctrl->handler, struct qcam, hdl); + int ret = 0; + + mutex_lock(&qcam->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + qcam->brightness = ctrl->val; + break; + case V4L2_CID_CONTRAST: + qcam->contrast = ctrl->val; + break; + case V4L2_CID_GAMMA: + qcam->whitebal = ctrl->val; + break; + default: + ret = -EINVAL; + break; + } + if (ret == 0) { + qc_setscanmode(qcam); + qcam->status |= QC_PARAM_CHANGE; + } + mutex_unlock(&qcam->lock); + return ret; +} + static const struct v4l2_file_operations qcam_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, .read = qcam_read, }; @@ -867,13 +838,17 @@ static const struct v4l2_ioctl_ops qcam_ioctl_ops = { .vidioc_g_input = qcam_g_input, .vidioc_s_input = qcam_s_input, .vidioc_enum_input = qcam_enum_input, - .vidioc_queryctrl = qcam_queryctrl, - .vidioc_g_ctrl = qcam_g_ctrl, - .vidioc_s_ctrl = qcam_s_ctrl, .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_ctrl_ops qcam_ctrl_ops = { + .s_ctrl = qcam_s_ctrl, }; /* Initialize the QuickCam driver control structure. This is where @@ -897,19 +872,35 @@ static struct qcam *qcam_init(struct parport *port) return NULL; } + v4l2_ctrl_handler_init(&qcam->hdl, 3); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 180); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 192); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_GAMMA, 0, 255, 1, 105); + if (qcam->hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + v4l2_ctrl_handler_free(&qcam->hdl); + kfree(qcam); + return NULL; + } qcam->pport = port; qcam->pdev = parport_register_device(port, "bw-qcam", NULL, NULL, NULL, 0, NULL); if (qcam->pdev == NULL) { v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); + v4l2_ctrl_handler_free(&qcam->hdl); kfree(qcam); return NULL; } strlcpy(qcam->vdev.name, "Connectix QuickCam", sizeof(qcam->vdev.name)); qcam->vdev.v4l2_dev = v4l2_dev; + qcam->vdev.ctrl_handler = &qcam->hdl; qcam->vdev.fops = &qcam_fops; qcam->vdev.ioctl_ops = &qcam_ioctl_ops; + set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); qcam->vdev.release = video_device_release_empty; video_set_drvdata(&qcam->vdev, qcam); @@ -1003,6 +994,7 @@ static int init_bwqcam(struct parport *port) static void close_bwqcam(struct qcam *qcam) { video_unregister_device(&qcam->vdev); + v4l2_ctrl_handler_free(&qcam->hdl); parport_unregister_device(qcam->pdev); kfree(qcam); } diff --git a/drivers/media/video/c-qcam.c b/drivers/media/video/c-qcam.c index fda32f52554a..ec51e1f12e82 100644 --- a/drivers/media/video/c-qcam.c +++ b/drivers/media/video/c-qcam.c @@ -40,10 +40,14 @@ #include #include #include +#include +#include +#include struct qcam { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler hdl; struct pardevice *pdev; struct parport *pport; int width, height; @@ -378,7 +382,7 @@ get_fragment: static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) { struct v4l2_device *v4l2_dev = &qcam->v4l2_dev; - unsigned lines, pixelsperline, bitsperxfer; + unsigned lines, pixelsperline; unsigned int is_bi_dir = qcam->bidirectional; size_t wantlen, outptr = 0; char tmpbuf[BUFSZ]; @@ -404,7 +408,6 @@ static long qc_capture(struct qcam *qcam, char __user *buf, unsigned long len) lines = qcam->height; pixelsperline = qcam->width; - bitsperxfer = (is_bi_dir) ? 24 : 8; if (is_bi_dir) { /* Turn the port around */ @@ -516,7 +519,8 @@ static int qcam_querycap(struct file *file, void *priv, strlcpy(vcap->driver, qcam->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, "Color Quickcam", sizeof(vcap->card)); strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -544,73 +548,6 @@ static int qcam_s_input(struct file *file, void *fh, unsigned int inp) return (inp > 0) ? -EINVAL : 0; } -static int qcam_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, 240); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 192); - case V4L2_CID_GAMMA: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - } - return -EINVAL; -} - -static int qcam_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct qcam *qcam = video_drvdata(file); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = qcam->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = qcam->contrast; - break; - case V4L2_CID_GAMMA: - ctrl->value = qcam->whitebal; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int qcam_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct qcam *qcam = video_drvdata(file); - int ret = 0; - - mutex_lock(&qcam->lock); - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - qcam->brightness = ctrl->value; - break; - case V4L2_CID_CONTRAST: - qcam->contrast = ctrl->value; - break; - case V4L2_CID_GAMMA: - qcam->whitebal = ctrl->value; - break; - default: - ret = -EINVAL; - break; - } - if (ret == 0) { - parport_claim_or_block(qcam->pdev); - qc_setup(qcam); - parport_release(qcam->pdev); - } - mutex_unlock(&qcam->lock); - return ret; -} - static int qcam_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct qcam *qcam = video_drvdata(file); @@ -714,8 +651,41 @@ static ssize_t qcam_read(struct file *file, char __user *buf, return len; } +static int qcam_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct qcam *qcam = + container_of(ctrl->handler, struct qcam, hdl); + int ret = 0; + + mutex_lock(&qcam->lock); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + qcam->brightness = ctrl->val; + break; + case V4L2_CID_CONTRAST: + qcam->contrast = ctrl->val; + break; + case V4L2_CID_GAMMA: + qcam->whitebal = ctrl->val; + break; + default: + ret = -EINVAL; + break; + } + if (ret == 0) { + parport_claim_or_block(qcam->pdev); + qc_setup(qcam); + parport_release(qcam->pdev); + } + mutex_unlock(&qcam->lock); + return ret; +} + static const struct v4l2_file_operations qcam_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, .read = qcam_read, }; @@ -725,13 +695,17 @@ static const struct v4l2_ioctl_ops qcam_ioctl_ops = { .vidioc_g_input = qcam_g_input, .vidioc_s_input = qcam_s_input, .vidioc_enum_input = qcam_enum_input, - .vidioc_queryctrl = qcam_queryctrl, - .vidioc_g_ctrl = qcam_g_ctrl, - .vidioc_s_ctrl = qcam_s_ctrl, - .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, + .vidioc_enum_fmt_vid_cap = qcam_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = qcam_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = qcam_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = qcam_try_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_ctrl_ops qcam_ctrl_ops = { + .s_ctrl = qcam_s_ctrl, }; /* Initialize the QuickCam driver control structure. */ @@ -754,6 +728,20 @@ static struct qcam *qcam_init(struct parport *port) return NULL; } + v4l2_ctrl_handler_init(&qcam->hdl, 3); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 240); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 192); + v4l2_ctrl_new_std(&qcam->hdl, &qcam_ctrl_ops, + V4L2_CID_GAMMA, 0, 255, 1, 128); + if (qcam->hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + v4l2_ctrl_handler_free(&qcam->hdl); + kfree(qcam); + return NULL; + } + qcam->pport = port; qcam->pdev = parport_register_device(port, "c-qcam", NULL, NULL, NULL, 0, NULL); @@ -762,6 +750,7 @@ static struct qcam *qcam_init(struct parport *port) if (qcam->pdev == NULL) { v4l2_err(v4l2_dev, "couldn't register for %s.\n", port->name); + v4l2_ctrl_handler_free(&qcam->hdl); kfree(qcam); return NULL; } @@ -771,6 +760,8 @@ static struct qcam *qcam_init(struct parport *port) qcam->vdev.fops = &qcam_fops; qcam->vdev.ioctl_ops = &qcam_ioctl_ops; qcam->vdev.release = video_device_release_empty; + qcam->vdev.ctrl_handler = &qcam->hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &qcam->vdev.flags); video_set_drvdata(&qcam->vdev, qcam); mutex_init(&qcam->lock); @@ -845,6 +836,7 @@ static int init_cqcam(struct parport *port) static void close_cqcam(struct qcam *qcam) { video_unregister_device(&qcam->vdev); + v4l2_ctrl_handler_free(&qcam->hdl); parport_unregister_device(qcam->pdev); kfree(qcam); } diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h index ab252188981b..cdef677d57ec 100644 --- a/drivers/media/video/cpia2/cpia2.h +++ b/drivers/media/video/cpia2/cpia2.h @@ -32,11 +32,12 @@ #define __CPIA2_H__ #include -#include #include #include +#include +#include +#include -#include "cpia2dev.h" #include "cpia2_registers.h" /* define for verbose debug output */ @@ -65,7 +66,6 @@ /* Flicker Modes */ #define NEVER_FLICKER 0 -#define ANTI_FLICKER_ON 1 #define FLICKER_60 60 #define FLICKER_50 50 @@ -148,7 +148,6 @@ enum { #define DEFAULT_BRIGHTNESS 0x46 #define DEFAULT_CONTRAST 0x93 #define DEFAULT_SATURATION 0x7f -#define DEFAULT_TARGET_KB 0x30 /* Power state */ #define HI_POWER_MODE CPIA2_SYSTEM_CONTROL_HIGH_POWER @@ -287,7 +286,6 @@ struct camera_params { struct { u8 cam_register; u8 flicker_mode_req; /* 1 if flicker on, else never flicker */ - int mains_frequency; } flicker_control; struct { @@ -337,7 +335,7 @@ struct camera_params { u8 vc_control; u8 vc_mp_direction; u8 vc_mp_data; - u8 target_kb; + u8 quality; } vc_params; struct { @@ -366,23 +364,23 @@ struct framebuf { struct framebuf *next; }; -struct cpia2_fh { - enum v4l2_priority prio; - u8 mmapped; -}; - struct camera_data { /* locks */ + struct v4l2_device v4l2_dev; struct mutex v4l2_lock; /* serialize file operations */ - struct v4l2_prio_state prio; + struct v4l2_ctrl_handler hdl; + struct { + /* Lights control cluster */ + struct v4l2_ctrl *top_light; + struct v4l2_ctrl *bottom_light; + }; + struct v4l2_ctrl *usb_alt; /* camera status */ - volatile int present; /* Is the camera still present? */ - int open_count; /* # of process that have camera open */ int first_image_seen; - u8 mains_freq; /* for flicker control */ enum sensors sensor_type; u8 flush; + struct v4l2_fh *stream_fh; u8 mmapped; int streaming; /* 0 = no, 1 = yes */ int xfer_mode; /* XFER_BULK or XFER_ISOC */ @@ -390,7 +388,7 @@ struct camera_data { /* v4l */ int video_size; /* VIDEO_SIZE_ */ - struct video_device *vdev; /* v4l videodev */ + struct video_device vdev; /* v4l videodev */ u32 width; u32 height; /* Its size */ __u32 pixelformat; /* Format fourcc */ @@ -425,6 +423,7 @@ struct camera_data { /* v4l */ int cpia2_register_camera(struct camera_data *cam); void cpia2_unregister_camera(struct camera_data *cam); +void cpia2_camera_release(struct v4l2_device *v4l2_dev); /* core */ int cpia2_reset_camera(struct camera_data *cam); @@ -443,7 +442,7 @@ int cpia2_send_command(struct camera_data *cam, struct cpia2_command *cmd); int cpia2_do_command(struct camera_data *cam, unsigned int command, unsigned char direction, unsigned char param); -struct camera_data *cpia2_init_camera_struct(void); +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf); int cpia2_init_camera(struct camera_data *cam); int cpia2_allocate_buffers(struct camera_data *cam); void cpia2_free_buffers(struct camera_data *cam); @@ -454,7 +453,6 @@ unsigned int cpia2_poll(struct camera_data *cam, int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma); void cpia2_set_property_flip(struct camera_data *cam, int prop_val); void cpia2_set_property_mirror(struct camera_data *cam, int prop_val); -int cpia2_set_target_kb(struct camera_data *cam, unsigned char value); int cpia2_set_gpio(struct camera_data *cam, unsigned char setting); int cpia2_set_fps(struct camera_data *cam, int framerate); diff --git a/drivers/media/video/cpia2/cpia2_core.c b/drivers/media/video/cpia2/cpia2_core.c index ee91e295c90a..17188e234770 100644 --- a/drivers/media/video/cpia2/cpia2_core.c +++ b/drivers/media/video/cpia2/cpia2_core.c @@ -66,7 +66,6 @@ static int config_sensor_410(struct camera_data *cam, static int config_sensor_500(struct camera_data *cam, int reqwidth, int reqheight); static int set_all_properties(struct camera_data *cam); -static void get_color_params(struct camera_data *cam); static void wake_system(struct camera_data *cam); static void set_lowlight_boost(struct camera_data *cam); static void reset_camera_struct(struct camera_data *cam); @@ -453,15 +452,6 @@ int cpia2_do_command(struct camera_data *cam, cam->params.version.vp_device_hi = cmd.buffer.block_data[0]; cam->params.version.vp_device_lo = cmd.buffer.block_data[1]; break; - case CPIA2_CMD_GET_VP_BRIGHTNESS: - cam->params.color_params.brightness = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_CONTRAST: - cam->params.color_params.contrast = cmd.buffer.block_data[0]; - break; - case CPIA2_CMD_GET_VP_SATURATION: - cam->params.color_params.saturation = cmd.buffer.block_data[0]; - break; case CPIA2_CMD_GET_VP_GPIO_DATA: cam->params.vp_params.gpio_data = cmd.buffer.block_data[0]; break; @@ -617,6 +607,7 @@ int cpia2_reset_camera(struct camera_data *cam) { u8 tmp_reg; int retval = 0; + int target_kb; int i; struct cpia2_command cmd; @@ -800,9 +791,16 @@ int cpia2_reset_camera(struct camera_data *cam) } cpia2_do_command(cam, CPIA2_CMD_SET_VC_CONTROL, TRANSFER_WRITE,tmp_reg); - /* Set target size (kb) on vc */ + /* Set target size (kb) on vc + This is a heuristic based on the quality parameter and the raw + framesize in kB divided by 16 (the compression factor when the + quality is 100%) */ + target_kb = (cam->width * cam->height * 2 / 16384) * + cam->params.vc_params.quality / 100; + if (target_kb < 1) + target_kb = 1; cpia2_do_command(cam, CPIA2_CMD_SET_TARGET_KB, - TRANSFER_WRITE, cam->params.vc_params.target_kb); + TRANSFER_WRITE, target_kb); /* Wiggle VC Reset */ /*** @@ -1538,23 +1536,17 @@ static int set_all_properties(struct camera_data *cam) * framerate and user_mode were already set (set_default_user_mode). **/ - cpia2_set_color_params(cam); - cpia2_usb_change_streaming_alternate(cam, cam->params.camera_state.stream_mode); - cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, - cam->params.vp_params.user_effects); - - cpia2_set_flicker_mode(cam, - cam->params.flicker_control.flicker_mode_req); - cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DIRECTION, TRANSFER_WRITE, cam->params.vp_params.gpio_direction); cpia2_do_command(cam, CPIA2_CMD_SET_VC_MP_GPIO_DATA, TRANSFER_WRITE, cam->params.vp_params.gpio_data); + v4l2_ctrl_handler_setup(&cam->hdl); + wake_system(cam); set_lowlight_boost(cam); @@ -1569,7 +1561,6 @@ static int set_all_properties(struct camera_data *cam) *****************************************************************************/ void cpia2_save_camera_state(struct camera_data *cam) { - get_color_params(cam); cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, TRANSFER_READ, 0); cpia2_do_command(cam, CPIA2_CMD_GET_VC_MP_GPIO_DIRECTION, TRANSFER_READ, 0); @@ -1577,30 +1568,6 @@ void cpia2_save_camera_state(struct camera_data *cam) /* Don't get framerate or target_kb. Trust the values we already have */ } -/****************************************************************************** - * - * get_color_params - * - *****************************************************************************/ -static void get_color_params(struct camera_data *cam) -{ - cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, TRANSFER_READ, 0); - cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST, TRANSFER_READ, 0); -} - -/****************************************************************************** - * - * cpia2_set_color_params - * - *****************************************************************************/ -void cpia2_set_color_params(struct camera_data *cam) -{ - DBG("Setting color params\n"); - cpia2_set_brightness(cam, cam->params.color_params.brightness); - cpia2_set_contrast(cam, cam->params.color_params.contrast); - cpia2_set_saturation(cam, cam->params.color_params.saturation); -} /****************************************************************************** * @@ -1664,15 +1631,9 @@ int cpia2_set_flicker_mode(struct camera_data *cam, int mode) switch(mode) { case NEVER_FLICKER: - cam->params.flicker_control.flicker_mode_req = mode; - break; case FLICKER_60: - cam->params.flicker_control.flicker_mode_req = mode; - cam->params.flicker_control.mains_frequency = 60; - break; case FLICKER_50: cam->params.flicker_control.flicker_mode_req = mode; - cam->params.flicker_control.mains_frequency = 50; break; default: err = -EINVAL; @@ -1701,6 +1662,7 @@ void cpia2_set_property_flip(struct camera_data *cam, int prop_val) { cam_reg &= ~CPIA2_VP_USER_EFFECTS_FLIP; } + cam->params.vp_params.user_effects = cam_reg; cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, cam_reg); } @@ -1725,35 +1687,11 @@ void cpia2_set_property_mirror(struct camera_data *cam, int prop_val) { cam_reg &= ~CPIA2_VP_USER_EFFECTS_MIRROR; } + cam->params.vp_params.user_effects = cam_reg; cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, cam_reg); } -/****************************************************************************** - * - * set_target_kb - * - * The new Target KB is set in cam->params.vc_params.target_kb and - * activates on reset. - *****************************************************************************/ - -int cpia2_set_target_kb(struct camera_data *cam, unsigned char value) -{ - DBG("Requested target_kb = %d\n", value); - if (value != cam->params.vc_params.target_kb) { - - cpia2_usb_stream_pause(cam); - - /* reset camera for new target_kb */ - cam->params.vc_params.target_kb = value; - cpia2_reset_camera(cam); - - cpia2_usb_stream_resume(cam); - } - - return 0; -} - /****************************************************************************** * * cpia2_set_gpio @@ -1843,7 +1781,7 @@ void cpia2_set_brightness(struct camera_data *cam, unsigned char value) if (cam->params.pnp_id.device_type == DEVICE_STV_672 && value == 0) value++; DBG("Setting brightness to %d (0x%0x)\n", value, value); - cpia2_do_command(cam,CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE,value); + cpia2_do_command(cam, CPIA2_CMD_SET_VP_BRIGHTNESS, TRANSFER_WRITE, value); } /****************************************************************************** @@ -1854,7 +1792,6 @@ void cpia2_set_brightness(struct camera_data *cam, unsigned char value) void cpia2_set_contrast(struct camera_data *cam, unsigned char value) { DBG("Setting contrast to %d (0x%0x)\n", value, value); - cam->params.color_params.contrast = value; cpia2_do_command(cam, CPIA2_CMD_SET_CONTRAST, TRANSFER_WRITE, value); } @@ -1866,7 +1803,6 @@ void cpia2_set_contrast(struct camera_data *cam, unsigned char value) void cpia2_set_saturation(struct camera_data *cam, unsigned char value) { DBG("Setting saturation to %d (0x%0x)\n", value, value); - cam->params.color_params.saturation = value; cpia2_do_command(cam,CPIA2_CMD_SET_VP_SATURATION, TRANSFER_WRITE,value); } @@ -2168,14 +2104,10 @@ static void reset_camera_struct(struct camera_data *cam) /*** * The following parameter values are the defaults from the register map. ***/ - cam->params.color_params.brightness = DEFAULT_BRIGHTNESS; - cam->params.color_params.contrast = DEFAULT_CONTRAST; - cam->params.color_params.saturation = DEFAULT_SATURATION; cam->params.vp_params.lowlight_boost = 0; /* FlickerModes */ cam->params.flicker_control.flicker_mode_req = NEVER_FLICKER; - cam->params.flicker_control.mains_frequency = 60; /* jpeg params */ cam->params.compression.jpeg_options = CPIA2_VC_VC_JPEG_OPT_DEFAULT; @@ -2188,7 +2120,7 @@ static void reset_camera_struct(struct camera_data *cam) cam->params.vp_params.gpio_data = 0; /* Target kb params */ - cam->params.vc_params.target_kb = DEFAULT_TARGET_KB; + cam->params.vc_params.quality = 100; /*** * Set Sensor FPS as fast as possible. @@ -2228,7 +2160,7 @@ static void reset_camera_struct(struct camera_data *cam) * * Initializes camera struct, does not call reset to fill in defaults. *****************************************************************************/ -struct camera_data *cpia2_init_camera_struct(void) +struct camera_data *cpia2_init_camera_struct(struct usb_interface *intf) { struct camera_data *cam; @@ -2239,8 +2171,13 @@ struct camera_data *cpia2_init_camera_struct(void) return NULL; } + cam->v4l2_dev.release = cpia2_camera_release; + if (v4l2_device_register(&intf->dev, &cam->v4l2_dev) < 0) { + v4l2_err(&cam->v4l2_dev, "couldn't register v4l2_device\n"); + kfree(cam); + return NULL; + } - cam->present = 1; mutex_init(&cam->v4l2_lock); init_waitqueue_head(&cam->wq_stream); @@ -2373,11 +2310,6 @@ long cpia2_read(struct camera_data *cam, return -EINVAL; } - if (!cam->present) { - LOG("%s: camera removed\n",__func__); - return 0; /* EOF */ - } - if (!cam->streaming) { /* Start streaming */ cpia2_usb_stream_start(cam, @@ -2393,12 +2325,12 @@ long cpia2_read(struct camera_data *cam, if (frame->status != FRAME_READY) { mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, - !cam->present || + !video_is_registered(&cam->vdev) || (frame = cam->curbuff)->status == FRAME_READY); mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if (!cam->present) + if (!video_is_registered(&cam->vdev)) return 0; } @@ -2423,17 +2355,10 @@ long cpia2_read(struct camera_data *cam, unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_table *wait) { - unsigned int status=0; + unsigned int status = v4l2_ctrl_poll(filp, wait); - if (!cam) { - ERR("%s: Internal error, camera_data not found!\n",__func__); - return POLLERR; - } - - if (!cam->present) - return POLLHUP; - - if(!cam->streaming) { + if ((poll_requested_events(wait) & (POLLIN | POLLRDNORM)) && + !cam->streaming) { /* Start streaming */ cpia2_usb_stream_start(cam, cam->params.camera_state.stream_mode); @@ -2441,10 +2366,8 @@ unsigned int cpia2_poll(struct camera_data *cam, struct file *filp, poll_wait(filp, &cam->wq_stream, wait); - if(!cam->present) - status = POLLHUP; - else if(cam->curbuff->status == FRAME_READY) - status = POLLIN | POLLRDNORM; + if (cam->curbuff->status == FRAME_READY) + status |= POLLIN | POLLRDNORM; return status; } @@ -2462,12 +2385,9 @@ int cpia2_remap_buffer(struct camera_data *cam, struct vm_area_struct *vma) unsigned long start = (unsigned long) adr; unsigned long page, pos; - if (!cam) - return -ENODEV; - DBG("mmap offset:%ld size:%ld\n", start_offset, size); - if (!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENODEV; if (size > cam->frame_size*cam->num_frames || diff --git a/drivers/media/video/cpia2/cpia2_usb.c b/drivers/media/video/cpia2/cpia2_usb.c index 59c797c15277..95b5d6e7cdc4 100644 --- a/drivers/media/video/cpia2/cpia2_usb.c +++ b/drivers/media/video/cpia2/cpia2_usb.c @@ -54,6 +54,8 @@ static void cpia2_usb_complete(struct urb *urb); static int cpia2_usb_probe(struct usb_interface *intf, const struct usb_device_id *id); static void cpia2_usb_disconnect(struct usb_interface *intf); +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message); +static int cpia2_usb_resume(struct usb_interface *intf); static void free_sbufs(struct camera_data *cam); static void add_APPn(struct camera_data *cam); @@ -74,6 +76,9 @@ static struct usb_driver cpia2_driver = { .name = "cpia2", .probe = cpia2_usb_probe, .disconnect = cpia2_usb_disconnect, + .suspend = cpia2_usb_suspend, + .resume = cpia2_usb_resume, + .reset_resume = cpia2_usb_resume, .id_table = cpia2_id_table }; @@ -218,10 +223,9 @@ static void cpia2_usb_complete(struct urb *urb) return; } - if (!cam->streaming || !cam->present || cam->open_count == 0) { - LOG("Will now stop the streaming: streaming = %d, " - "present=%d, open_count=%d\n", - cam->streaming, cam->present, cam->open_count); + if (!cam->streaming || !video_is_registered(&cam->vdev)) { + LOG("Will now stop the streaming: streaming = %d, present=%d\n", + cam->streaming, video_is_registered(&cam->vdev)); return; } @@ -392,7 +396,7 @@ static int configure_transfer_mode(struct camera_data *cam, unsigned int alt) struct cpia2_command cmd; unsigned char reg; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENODEV; /*** @@ -752,8 +756,8 @@ int cpia2_usb_stream_pause(struct camera_data *cam) { int ret = 0; if(cam->streaming) { - ret = set_alternate(cam, USBIF_CMDONLY); free_sbufs(cam); + ret = set_alternate(cam, USBIF_CMDONLY); } return ret; } @@ -770,6 +774,10 @@ int cpia2_usb_stream_resume(struct camera_data *cam) cam->first_image_seen = 0; ret = set_alternate(cam, cam->params.camera_state.stream_mode); if(ret == 0) { + /* for some reason the user effects need to be set + again when starting streaming. */ + cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE, + cam->params.vp_params.user_effects); ret = submit_urbs(cam); } } @@ -784,6 +792,7 @@ int cpia2_usb_stream_resume(struct camera_data *cam) int cpia2_usb_stream_stop(struct camera_data *cam) { int ret; + ret = cpia2_usb_stream_pause(cam); cam->streaming = 0; configure_transfer_mode(cam, 0); @@ -812,7 +821,8 @@ static int cpia2_usb_probe(struct usb_interface *intf, /* If we get to this point, we found a CPiA2 camera */ LOG("CPiA2 USB camera found\n"); - if((cam = cpia2_init_camera_struct()) == NULL) + cam = cpia2_init_camera_struct(intf); + if (cam == NULL) return -ENOMEM; cam->dev = udev; @@ -825,16 +835,9 @@ static int cpia2_usb_probe(struct usb_interface *intf, return ret; } - if ((ret = cpia2_register_camera(cam)) < 0) { - ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); - kfree(cam); - return ret; - } - if((ret = cpia2_init_camera(cam)) < 0) { ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret); - cpia2_unregister_camera(cam); kfree(cam); return ret; } @@ -853,6 +856,13 @@ static int cpia2_usb_probe(struct usb_interface *intf, usb_set_intfdata(intf, cam); + ret = cpia2_register_camera(cam); + if (ret < 0) { + ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret); + kfree(cam); + return ret; + } + return 0; } @@ -865,13 +875,16 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) { struct camera_data *cam = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL); - cam->present = 0; DBG("Stopping stream\n"); cpia2_usb_stream_stop(cam); + mutex_lock(&cam->v4l2_lock); DBG("Unregistering camera\n"); cpia2_unregister_camera(cam); + v4l2_device_disconnect(&cam->v4l2_dev); + mutex_unlock(&cam->v4l2_lock); + v4l2_device_put(&cam->v4l2_dev); if(cam->buffers) { DBG("Wakeup waiting processes\n"); @@ -884,14 +897,41 @@ static void cpia2_usb_disconnect(struct usb_interface *intf) DBG("Releasing interface\n"); usb_driver_release_interface(&cpia2_driver, intf); - if (cam->open_count == 0) { - DBG("Freeing camera structure\n"); - kfree(cam); - } - LOG("CPiA2 camera disconnected.\n"); } +static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + if (cam->streaming) { + cpia2_usb_stream_stop(cam); + cam->streaming = 1; + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "going into suspend..\n"); + return 0; +} + +/* Resume device - start device. */ +static int cpia2_usb_resume(struct usb_interface *intf) +{ + struct camera_data *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->v4l2_lock); + v4l2_ctrl_handler_setup(&cam->hdl); + if (cam->streaming) { + cam->streaming = 0; + cpia2_usb_stream_start(cam, + cam->params.camera_state.stream_mode); + } + mutex_unlock(&cam->v4l2_lock); + + dev_info(&intf->dev, "coming out of suspend..\n"); + return 0; +} /****************************************************************************** * diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c index 077eb1db80a1..55e92902a76c 100644 --- a/drivers/media/video/cpia2/cpia2_v4l.c +++ b/drivers/media/video/cpia2/cpia2_v4l.c @@ -39,15 +39,15 @@ #include #include #include +#include #include "cpia2.h" -#include "cpia2dev.h" static int video_nr = -1; module_param(video_nr, int, 0); -MODULE_PARM_DESC(video_nr,"video device to register (0=/dev/video0, etc)"); +MODULE_PARM_DESC(video_nr, "video device to register (0=/dev/video0, etc)"); -static int buffer_size = 68*1024; +static int buffer_size = 68 * 1024; module_param(buffer_size, int, 0); MODULE_PARM_DESC(buffer_size, "Size for each frame buffer in bytes (default 68k)"); @@ -62,18 +62,10 @@ MODULE_PARM_DESC(alternate, "USB Alternate (" __stringify(USBIF_ISO_1) "-" __stringify(USBIF_ISO_6) ", default " __stringify(DEFAULT_ALT) ")"); -static int flicker_freq = 60; -module_param(flicker_freq, int, 0); -MODULE_PARM_DESC(flicker_freq, "Flicker frequency (" __stringify(50) "or" - __stringify(60) ", default " - __stringify(60) ")"); - -static int flicker_mode = NEVER_FLICKER; +static int flicker_mode; module_param(flicker_mode, int, 0); -MODULE_PARM_DESC(flicker_mode, - "Flicker supression (" __stringify(NEVER_FLICKER) "or" - __stringify(ANTI_FLICKER_ON) ", default " - __stringify(NEVER_FLICKER) ")"); +MODULE_PARM_DESC(flicker_mode, "Flicker frequency (0 (disabled), " __stringify(50) " or " + __stringify(60) ", default 0)"); MODULE_AUTHOR("Steve Miller (STMicroelectronics) "); MODULE_DESCRIPTION("V4L-driver for STMicroelectronics CPiA2 based cameras"); @@ -82,153 +74,7 @@ MODULE_LICENSE("GPL"); MODULE_VERSION(CPIA_VERSION); #define ABOUT "V4L-Driver for Vision CPiA2 based cameras" - -struct control_menu_info { - int value; - char name[32]; -}; - -static struct control_menu_info framerate_controls[] = -{ - { CPIA2_VP_FRAMERATE_6_25, "6.25 fps" }, - { CPIA2_VP_FRAMERATE_7_5, "7.5 fps" }, - { CPIA2_VP_FRAMERATE_12_5, "12.5 fps" }, - { CPIA2_VP_FRAMERATE_15, "15 fps" }, - { CPIA2_VP_FRAMERATE_25, "25 fps" }, - { CPIA2_VP_FRAMERATE_30, "30 fps" }, -}; -#define NUM_FRAMERATE_CONTROLS (ARRAY_SIZE(framerate_controls)) - -static struct control_menu_info flicker_controls[] = -{ - { NEVER_FLICKER, "Off" }, - { FLICKER_50, "50 Hz" }, - { FLICKER_60, "60 Hz" }, -}; -#define NUM_FLICKER_CONTROLS (ARRAY_SIZE(flicker_controls)) - -static struct control_menu_info lights_controls[] = -{ - { 0, "Off" }, - { 64, "Top" }, - { 128, "Bottom" }, - { 192, "Both" }, -}; -#define NUM_LIGHTS_CONTROLS (ARRAY_SIZE(lights_controls)) -#define GPIO_LIGHTS_MASK 192 - -static struct v4l2_queryctrl controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_BRIGHTNESS, - }, - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_CONTRAST, - }, - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_SATURATION, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mirror Horizontally", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Flip Vertically", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_TARGET_KB, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Target KB", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = DEFAULT_TARGET_KB, - }, - { - .id = CPIA2_CID_GPIO, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "GPIO", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_FLICKER_MODE, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Flicker Reduction", - .minimum = 0, - .maximum = NUM_FLICKER_CONTROLS-1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_FRAMERATE, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Framerate", - .minimum = 0, - .maximum = NUM_FRAMERATE_CONTROLS-1, - .step = 1, - .default_value = NUM_FRAMERATE_CONTROLS-1, - }, - { - .id = CPIA2_CID_USB_ALT, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "USB Alternate", - .minimum = USBIF_ISO_1, - .maximum = USBIF_ISO_6, - .step = 1, - .default_value = DEFAULT_ALT, - }, - { - .id = CPIA2_CID_LIGHTS, - .type = V4L2_CTRL_TYPE_MENU, - .name = "Lights", - .minimum = 0, - .maximum = NUM_LIGHTS_CONTROLS-1, - .step = 1, - .default_value = 0, - }, - { - .id = CPIA2_CID_RESET_CAMERA, - .type = V4L2_CTRL_TYPE_BUTTON, - .name = "Reset Camera", - .minimum = 0, - .maximum = 0, - .step = 0, - .default_value = 0, - }, -}; -#define NUM_CONTROLS (ARRAY_SIZE(controls)) - +#define CPIA2_CID_USB_ALT (V4L2_CID_USER_BASE | 0xf000) /****************************************************************************** * @@ -238,38 +84,27 @@ static struct v4l2_queryctrl controls[] = { static int cpia2_open(struct file *file) { struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh; + int retval = v4l2_fh_open(file); - if (!cam) { - ERR("Internal error, camera_data not found!\n"); - return -ENODEV; - } + if (retval) + return retval; - if (!cam->present) - return -ENODEV; - - if (cam->open_count == 0) { - if (cpia2_allocate_buffers(cam)) + if (v4l2_fh_is_singular_file(file)) { + if (cpia2_allocate_buffers(cam)) { + v4l2_fh_release(file); return -ENOMEM; + } /* reset the camera */ - if (cpia2_reset_camera(cam) < 0) + if (cpia2_reset_camera(cam) < 0) { + v4l2_fh_release(file); return -EIO; + } cam->APP_len = 0; cam->COM_len = 0; } - fh = kmalloc(sizeof(*fh), GFP_KERNEL); - if (!fh) - return -ENOMEM; - file->private_data = fh; - fh->prio = V4L2_PRIORITY_UNSET; - v4l2_prio_open(&cam->prio, &fh->prio); - fh->mmapped = 0; - - ++cam->open_count; - cpia2_dbg_dump_registers(cam); return 0; } @@ -283,37 +118,22 @@ static int cpia2_close(struct file *file) { struct video_device *dev = video_devdata(file); struct camera_data *cam = video_get_drvdata(dev); - struct cpia2_fh *fh = file->private_data; - if (cam->present && - (cam->open_count == 1 || fh->prio == V4L2_PRIORITY_RECORD)) { + if (video_is_registered(&cam->vdev) && v4l2_fh_is_singular_file(file)) { cpia2_usb_stream_stop(cam); - if (cam->open_count == 1) { - /* save camera state for later open */ - cpia2_save_camera_state(cam); + /* save camera state for later open */ + cpia2_save_camera_state(cam); - cpia2_set_low_power(cam); - cpia2_free_buffers(cam); - } - } - - if (fh->mmapped) - cam->mmapped = 0; - v4l2_prio_close(&cam->prio, fh->prio); - file->private_data = NULL; - kfree(fh); - - if (--cam->open_count == 0) { + cpia2_set_low_power(cam); cpia2_free_buffers(cam); - if (!cam->present) { - video_unregister_device(dev); - kfree(cam); - return 0; - } } - return 0; + if (cam->stream_fh == file->private_data) { + cam->stream_fh = NULL; + cam->mmapped = 0; + } + return v4l2_fh_release(file); } /****************************************************************************** @@ -327,16 +147,9 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, struct camera_data *cam = video_drvdata(file); int noblock = file->f_flags&O_NONBLOCK; - struct cpia2_fh *fh = file->private_data; - if(!cam) return -EINVAL; - /* Priority check */ - if(fh->prio != V4L2_PRIORITY_RECORD) { - return -EBUSY; - } - return cpia2_read(cam, buf, count, noblock); } @@ -349,15 +162,6 @@ static ssize_t cpia2_v4l_read(struct file *file, char __user *buf, size_t count, static unsigned int cpia2_v4l_poll(struct file *filp, struct poll_table_struct *wait) { struct camera_data *cam = video_drvdata(filp); - struct cpia2_fh *fh = filp->private_data; - - if(!cam) - return POLLERR; - - /* Priority check */ - if(fh->prio != V4L2_PRIORITY_RECORD) { - return POLLERR; - } return cpia2_poll(cam, filp, wait); } @@ -384,34 +188,11 @@ static int sync(struct camera_data *cam, int frame_nr) mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENOTTY; } } -/****************************************************************************** - * - * ioctl_set_gpio - * - *****************************************************************************/ - -static long cpia2_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) -{ - struct camera_data *cam = video_drvdata(file); - __u32 gpio_val; - - if (cmd != CPIA2_CID_GPIO) - return -EINVAL; - - gpio_val = *(__u32*) arg; - - if (gpio_val &~ 0xFFU) - return -EINVAL; - - return cpia2_set_gpio(cam, (unsigned char)gpio_val); -} - /****************************************************************************** * * ioctl_querycap @@ -465,9 +246,11 @@ static int cpia2_querycap(struct file *file, void *fh, struct v4l2_capability *v if (usb_make_path(cam->dev, vc->bus_info, sizeof(vc->bus_info)) <0) memset(vc->bus_info,0, sizeof(vc->bus_info)); - vc->capabilities = V4L2_CAP_VIDEO_CAPTURE | + vc->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + vc->capabilities = vc->device_caps | + V4L2_CAP_DEVICE_CAPS; return 0; } @@ -610,22 +393,12 @@ static int cpia2_s_fmt_vid_cap(struct file *file, void *_fh, struct v4l2_format *f) { struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh = _fh; int err, frame; - err = v4l2_prio_check(&cam->prio, fh->prio); - if (err) - return err; err = cpia2_try_fmt_vid_cap(file, _fh, f); if(err != 0) return err; - /* Ensure that only this process can change the format. */ - err = v4l2_prio_change(&cam->prio, &fh->prio, V4L2_PRIORITY_RECORD); - if(err != 0) { - return err; - } - cam->pixelformat = f->fmt.pix.pixelformat; /* NOTE: This should be set to 1 for MJPEG, but some apps don't handle @@ -713,240 +486,126 @@ static int cpia2_cropcap(struct file *file, void *fh, struct v4l2_cropcap *c) return 0; } -/****************************************************************************** - * - * ioctl_queryctrl - * - * V4L2 query possible control variables - * - *****************************************************************************/ +struct framerate_info { + int value; + struct v4l2_fract period; +}; -static int cpia2_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c) +static const struct framerate_info framerate_controls[] = { + { CPIA2_VP_FRAMERATE_6_25, { 4, 25 } }, + { CPIA2_VP_FRAMERATE_7_5, { 2, 15 } }, + { CPIA2_VP_FRAMERATE_12_5, { 2, 25 } }, + { CPIA2_VP_FRAMERATE_15, { 1, 15 } }, + { CPIA2_VP_FRAMERATE_25, { 1, 25 } }, + { CPIA2_VP_FRAMERATE_30, { 1, 30 } }, +}; + +static int cpia2_g_parm(struct file *file, void *fh, struct v4l2_streamparm *p) { struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; int i; - for(i=0; iid == controls[i].id) { - memcpy(c, controls+i, sizeof(*c)); + if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + cap->capability = V4L2_CAP_TIMEPERFRAME; + cap->readbuffers = cam->num_frames; + for (i = 0; i < ARRAY_SIZE(framerate_controls); i++) + if (cam->params.vp_params.frame_rate == framerate_controls[i].value) { + cap->timeperframe = framerate_controls[i].period; break; } - } + return 0; +} - if(i == NUM_CONTROLS) +static int cpia2_s_parm(struct file *file, void *fh, struct v4l2_streamparm *p) +{ + struct camera_data *cam = video_drvdata(file); + struct v4l2_captureparm *cap = &p->parm.capture; + struct v4l2_fract tpf = cap->timeperframe; + int max = ARRAY_SIZE(framerate_controls) - 1; + int ret; + int i; + + ret = cpia2_g_parm(file, fh, p); + if (ret || !tpf.denominator || !tpf.numerator) + return ret; + + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + for (i = 0; i <= max; i++) { + struct v4l2_fract f1 = tpf; + struct v4l2_fract f2 = framerate_controls[i].period; + + f1.numerator *= f2.denominator; + f2.numerator *= f1.denominator; + if (f1.numerator >= f2.numerator) + break; + } + if (i > max) + i = max; + cap->timeperframe = framerate_controls[i].period; + return cpia2_set_fps(cam, framerate_controls[i].value); +} + +static const struct { + u32 width; + u32 height; +} cpia2_framesizes[] = { + { 640, 480 }, + { 352, 288 }, + { 320, 240 }, + { 288, 216 }, + { 256, 192 }, + { 224, 168 }, + { 192, 144 }, + { 176, 144 }, +}; + +static int cpia2_enum_framesizes(struct file *file, void *fh, + struct v4l2_frmsizeenum *fsize) +{ + + if (fsize->pixel_format != V4L2_PIX_FMT_MJPEG && + fsize->pixel_format != V4L2_PIX_FMT_JPEG) return -EINVAL; - - /* Some devices have additional limitations */ - switch(c->id) { - case V4L2_CID_BRIGHTNESS: - /*** - * Don't let the register be set to zero - bug in VP4 - * flash of full brightness - ***/ - if (cam->params.pnp_id.device_type == DEVICE_STV_672) - c->minimum = 1; - break; - case V4L2_CID_VFLIP: - // VP5 Only - if(cam->params.pnp_id.device_type == DEVICE_STV_672) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - case CPIA2_CID_FRAMERATE: - if(cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){ - // Maximum 15fps - for(i=0; imaximum; ++i) { - if(framerate_controls[i].value == - CPIA2_VP_FRAMERATE_15) { - c->maximum = i; - c->default_value = i; - } - } - } - break; - case CPIA2_CID_FLICKER_MODE: - // Flicker control only valid for 672. - if(cam->params.pnp_id.device_type != DEVICE_STV_672) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - case CPIA2_CID_LIGHTS: - // Light control only valid for the QX5 Microscope. - if(cam->params.pnp_id.product != 0x151) - c->flags |= V4L2_CTRL_FLAG_DISABLED; - break; - default: - break; - } + if (fsize->index >= ARRAY_SIZE(cpia2_framesizes)) + return -EINVAL; + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = cpia2_framesizes[fsize->index].width; + fsize->discrete.height = cpia2_framesizes[fsize->index].height; return 0; } -/****************************************************************************** - * - * ioctl_querymenu - * - * V4L2 query possible control variables - * - *****************************************************************************/ - -static int cpia2_querymenu(struct file *file, void *fh, struct v4l2_querymenu *m) +static int cpia2_enum_frameintervals(struct file *file, void *fh, + struct v4l2_frmivalenum *fival) { struct camera_data *cam = video_drvdata(file); + int max = ARRAY_SIZE(framerate_controls) - 1; + int i; - switch(m->id) { - case CPIA2_CID_FLICKER_MODE: - if (m->index >= NUM_FLICKER_CONTROLS) - return -EINVAL; - - strcpy(m->name, flicker_controls[m->index].name); - break; - case CPIA2_CID_FRAMERATE: - { - int maximum = NUM_FRAMERATE_CONTROLS - 1; - if(cam->params.pnp_id.device_type == DEVICE_STV_672 && - cam->params.version.sensor_flags==CPIA2_VP_SENSOR_FLAGS_500){ - // Maximum 15fps - int i; - for(i=0; iindex > maximum) - return -EINVAL; - - strcpy(m->name, framerate_controls[m->index].name); - break; - } - case CPIA2_CID_LIGHTS: - if (m->index >= NUM_LIGHTS_CONTROLS) - return -EINVAL; - - strcpy(m->name, lights_controls[m->index].name); - break; - default: + if (fival->pixel_format != V4L2_PIX_FMT_MJPEG && + fival->pixel_format != V4L2_PIX_FMT_JPEG) return -EINVAL; - } - return 0; -} - -/****************************************************************************** - * - * ioctl_g_ctrl - * - * V4L2 get the value of a control variable - * - *****************************************************************************/ - -static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) -{ - struct camera_data *cam = video_drvdata(file); - - switch(c->id) { - case V4L2_CID_BRIGHTNESS: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_BRIGHTNESS, - TRANSFER_READ, 0); - c->value = cam->params.color_params.brightness; - break; - case V4L2_CID_CONTRAST: - cpia2_do_command(cam, CPIA2_CMD_GET_CONTRAST, - TRANSFER_READ, 0); - c->value = cam->params.color_params.contrast; - break; - case V4L2_CID_SATURATION: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_SATURATION, - TRANSFER_READ, 0); - c->value = cam->params.color_params.saturation; - break; - case V4L2_CID_HFLIP: - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, - TRANSFER_READ, 0); - c->value = (cam->params.vp_params.user_effects & - CPIA2_VP_USER_EFFECTS_MIRROR) != 0; - break; - case V4L2_CID_VFLIP: - cpia2_do_command(cam, CPIA2_CMD_GET_USER_EFFECTS, - TRANSFER_READ, 0); - c->value = (cam->params.vp_params.user_effects & - CPIA2_VP_USER_EFFECTS_FLIP) != 0; - break; - case CPIA2_CID_TARGET_KB: - c->value = cam->params.vc_params.target_kb; - break; - case CPIA2_CID_GPIO: - cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA, - TRANSFER_READ, 0); - c->value = cam->params.vp_params.gpio_data; - break; - case CPIA2_CID_FLICKER_MODE: - { - int i, mode; - cpia2_do_command(cam, CPIA2_CMD_GET_FLICKER_MODES, - TRANSFER_READ, 0); - if(cam->params.flicker_control.cam_register & - CPIA2_VP_FLICKER_MODES_NEVER_FLICKER) { - mode = NEVER_FLICKER; - } else { - if(cam->params.flicker_control.cam_register & - CPIA2_VP_FLICKER_MODES_50HZ) { - mode = FLICKER_50; - } else { - mode = FLICKER_60; - } - } - for(i=0; ivalue = i; - break; - } - } - if(i == NUM_FLICKER_CONTROLS) - return -EINVAL; - break; - } - case CPIA2_CID_FRAMERATE: - { - int maximum = NUM_FRAMERATE_CONTROLS - 1; - int i; - for(i=0; i<= maximum; i++) { - if(cam->params.vp_params.frame_rate == - framerate_controls[i].value) - break; - } - if(i > maximum) - return -EINVAL; - c->value = i; - break; - } - case CPIA2_CID_USB_ALT: - c->value = cam->params.camera_state.stream_mode; - break; - case CPIA2_CID_LIGHTS: - { - int i; - cpia2_do_command(cam, CPIA2_CMD_GET_VP_GPIO_DATA, - TRANSFER_READ, 0); - for(i=0; iparams.vp_params.gpio_data&GPIO_LIGHTS_MASK) == - lights_controls[i].value) { - break; - } - } - if(i == NUM_LIGHTS_CONTROLS) - return -EINVAL; - c->value = i; - break; - } - case CPIA2_CID_RESET_CAMERA: + /* Maximum 15 fps for this model */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672 && + cam->params.version.sensor_flags == CPIA2_VP_SENSOR_FLAGS_500) + max -= 2; + if (fival->index > max) return -EINVAL; - default: + for (i = 0; i < ARRAY_SIZE(cpia2_framesizes); i++) + if (fival->width == cpia2_framesizes[i].width && + fival->height == cpia2_framesizes[i].height) + break; + if (i == ARRAY_SIZE(cpia2_framesizes)) return -EINVAL; - } - - DBG("Get control id:%d, value:%d\n", c->id, c->value); - + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete = framerate_controls[fival->index].period; return 0; } @@ -958,72 +617,54 @@ static int cpia2_g_ctrl(struct file *file, void *fh, struct v4l2_control *c) * *****************************************************************************/ -static int cpia2_s_ctrl(struct file *file, void *fh, struct v4l2_control *c) +static int cpia2_s_ctrl(struct v4l2_ctrl *ctrl) { - struct camera_data *cam = video_drvdata(file); - int i; - int retval = 0; + struct camera_data *cam = + container_of(ctrl->handler, struct camera_data, hdl); + static const int flicker_table[] = { + NEVER_FLICKER, + FLICKER_50, + FLICKER_60, + }; - DBG("Set control id:%d, value:%d\n", c->id, c->value); + DBG("Set control id:%d, value:%d\n", ctrl->id, ctrl->val); - /* Check that the value is in range */ - for(i=0; iid == controls[i].id) { - if(c->value < controls[i].minimum || - c->value > controls[i].maximum) { - return -EINVAL; - } - break; - } - } - if(i == NUM_CONTROLS) - return -EINVAL; - - switch(c->id) { + switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - cpia2_set_brightness(cam, c->value); + cpia2_set_brightness(cam, ctrl->val); break; case V4L2_CID_CONTRAST: - cpia2_set_contrast(cam, c->value); + cpia2_set_contrast(cam, ctrl->val); break; case V4L2_CID_SATURATION: - cpia2_set_saturation(cam, c->value); + cpia2_set_saturation(cam, ctrl->val); break; case V4L2_CID_HFLIP: - cpia2_set_property_mirror(cam, c->value); + cpia2_set_property_mirror(cam, ctrl->val); break; case V4L2_CID_VFLIP: - cpia2_set_property_flip(cam, c->value); + cpia2_set_property_flip(cam, ctrl->val); break; - case CPIA2_CID_TARGET_KB: - retval = cpia2_set_target_kb(cam, c->value); + case V4L2_CID_POWER_LINE_FREQUENCY: + return cpia2_set_flicker_mode(cam, flicker_table[ctrl->val]); + case V4L2_CID_ILLUMINATORS_1: + return cpia2_set_gpio(cam, (cam->top_light->val << 6) | + (cam->bottom_light->val << 7)); + case V4L2_CID_JPEG_ACTIVE_MARKER: + cam->params.compression.inhibit_htables = + !(ctrl->val & V4L2_JPEG_ACTIVE_MARKER_DHT); break; - case CPIA2_CID_GPIO: - retval = cpia2_set_gpio(cam, c->value); - break; - case CPIA2_CID_FLICKER_MODE: - retval = cpia2_set_flicker_mode(cam, - flicker_controls[c->value].value); - break; - case CPIA2_CID_FRAMERATE: - retval = cpia2_set_fps(cam, framerate_controls[c->value].value); + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + cam->params.vc_params.quality = ctrl->val; break; case CPIA2_CID_USB_ALT: - retval = cpia2_usb_change_streaming_alternate(cam, c->value); - break; - case CPIA2_CID_LIGHTS: - retval = cpia2_set_gpio(cam, lights_controls[c->value].value); - break; - case CPIA2_CID_RESET_CAMERA: - cpia2_usb_stream_pause(cam); - cpia2_reset_camera(cam); - cpia2_usb_stream_resume(cam); + cam->params.camera_state.stream_mode = ctrl->val; break; default: - retval = -EINVAL; + return -EINVAL; } - return retval; + return 0; } /****************************************************************************** @@ -1084,6 +725,8 @@ static int cpia2_s_jpegcomp(struct file *file, void *fh, struct v4l2_jpegcompres cam->params.compression.inhibit_htables = !(parms->jpeg_markers & V4L2_JPEG_MARKER_DHT); + parms->jpeg_markers &= V4L2_JPEG_MARKER_DQT | V4L2_JPEG_MARKER_DRI | + V4L2_JPEG_MARKER_DHT; if(parms->APP_len != 0) { if(parms->APP_len > 0 && @@ -1270,12 +913,12 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) struct framebuf *cb=cam->curbuff; mutex_unlock(&cam->v4l2_lock); wait_event_interruptible(cam->wq_stream, - !cam->present || + !video_is_registered(&cam->vdev) || (cb=cam->curbuff)->status == FRAME_READY); mutex_lock(&cam->v4l2_lock); if (signal_pending(current)) return -ERESTARTSYS; - if(!cam->present) + if (!video_is_registered(&cam->vdev)) return -ENOTTY; frame = cb->num; } @@ -1299,56 +942,39 @@ static int cpia2_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) return 0; } -static int cpia2_g_priority(struct file *file, void *_fh, enum v4l2_priority *p) -{ - struct cpia2_fh *fh = _fh; - - *p = fh->prio; - return 0; -} - -static int cpia2_s_priority(struct file *file, void *_fh, enum v4l2_priority prio) -{ - struct camera_data *cam = video_drvdata(file); - struct cpia2_fh *fh = _fh; - - if (cam->streaming && prio != fh->prio && - fh->prio == V4L2_PRIORITY_RECORD) - /* Can't drop record priority while streaming */ - return -EBUSY; - - if (prio == V4L2_PRIORITY_RECORD && prio != fh->prio && - v4l2_prio_max(&cam->prio) == V4L2_PRIORITY_RECORD) - /* Only one program can record at a time */ - return -EBUSY; - return v4l2_prio_change(&cam->prio, &fh->prio, prio); -} - static int cpia2_streamon(struct file *file, void *fh, enum v4l2_buf_type type) { struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; DBG("VIDIOC_STREAMON, streaming=%d\n", cam->streaming); if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (!cam->streaming) - return cpia2_usb_stream_start(cam, + if (!cam->streaming) { + ret = cpia2_usb_stream_start(cam, cam->params.camera_state.stream_mode); - return -EINVAL; + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, true); + } + return ret; } static int cpia2_streamoff(struct file *file, void *fh, enum v4l2_buf_type type) { struct camera_data *cam = video_drvdata(file); + int ret = -EINVAL; DBG("VIDIOC_STREAMOFF, streaming=%d\n", cam->streaming); if (!cam->mmapped || type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - if (cam->streaming) - return cpia2_usb_stream_stop(cam); - return -EINVAL; + if (cam->streaming) { + ret = cpia2_usb_stream_stop(cam); + if (!ret) + v4l2_ctrl_grab(cam->usb_alt, false); + } + return ret; } /****************************************************************************** @@ -1361,16 +987,10 @@ static int cpia2_mmap(struct file *file, struct vm_area_struct *area) struct camera_data *cam = video_drvdata(file); int retval; - /* Priority check */ - struct cpia2_fh *fh = file->private_data; - if(fh->prio != V4L2_PRIORITY_RECORD) { - return -EBUSY; - } - retval = cpia2_remap_buffer(cam, area); if(!retval) - fh->mmapped = 1; + cam->stream_fh = file->private_data; return retval; } @@ -1388,15 +1008,13 @@ static void reset_camera_struct_v4l(struct camera_data *cam) cam->frame_size = buffer_size; cam->num_frames = num_buffers; - /* FlickerModes */ + /* Flicker modes */ cam->params.flicker_control.flicker_mode_req = flicker_mode; - cam->params.flicker_control.mains_frequency = flicker_freq; - /* streamMode */ + /* stream modes */ cam->params.camera_state.stream_mode = alternate; cam->pixelformat = V4L2_PIX_FMT_JPEG; - v4l2_prio_init(&cam->prio); } static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { @@ -1408,10 +1026,6 @@ static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { .vidioc_g_fmt_vid_cap = cpia2_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = cpia2_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = cpia2_try_fmt_vid_cap, - .vidioc_queryctrl = cpia2_queryctrl, - .vidioc_querymenu = cpia2_querymenu, - .vidioc_g_ctrl = cpia2_g_ctrl, - .vidioc_s_ctrl = cpia2_s_ctrl, .vidioc_g_jpegcomp = cpia2_g_jpegcomp, .vidioc_s_jpegcomp = cpia2_s_jpegcomp, .vidioc_cropcap = cpia2_cropcap, @@ -1421,9 +1035,12 @@ static const struct v4l2_ioctl_ops cpia2_ioctl_ops = { .vidioc_dqbuf = cpia2_dqbuf, .vidioc_streamon = cpia2_streamon, .vidioc_streamoff = cpia2_streamoff, - .vidioc_g_priority = cpia2_g_priority, - .vidioc_s_priority = cpia2_s_priority, - .vidioc_default = cpia2_default, + .vidioc_s_parm = cpia2_s_parm, + .vidioc_g_parm = cpia2_g_parm, + .vidioc_enum_framesizes = cpia2_enum_framesizes, + .vidioc_enum_frameintervals = cpia2_enum_frameintervals, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /*** @@ -1444,7 +1061,21 @@ static struct video_device cpia2_template = { .name = "CPiA2 Camera", .fops = &cpia2_fops, .ioctl_ops = &cpia2_ioctl_ops, - .release = video_device_release, + .release = video_device_release_empty, +}; + +void cpia2_camera_release(struct v4l2_device *v4l2_dev) +{ + struct camera_data *cam = + container_of(v4l2_dev, struct camera_data, v4l2_dev); + + v4l2_ctrl_handler_free(&cam->hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); +} + +static const struct v4l2_ctrl_ops cpia2_ctrl_ops = { + .s_ctrl = cpia2_s_ctrl, }; /****************************************************************************** @@ -1454,20 +1085,78 @@ static struct video_device cpia2_template = { *****************************************************************************/ int cpia2_register_camera(struct camera_data *cam) { - cam->vdev = video_device_alloc(); - if(!cam->vdev) - return -ENOMEM; + struct v4l2_ctrl_handler *hdl = &cam->hdl; + struct v4l2_ctrl_config cpia2_usb_alt = { + .ops = &cpia2_ctrl_ops, + .id = CPIA2_CID_USB_ALT, + .name = "USB Alternate", + .type = V4L2_CTRL_TYPE_INTEGER, + .min = USBIF_ISO_1, + .max = USBIF_ISO_6, + .step = 1, + }; + int ret; - memcpy(cam->vdev, &cpia2_template, sizeof(cpia2_template)); - video_set_drvdata(cam->vdev, cam); - cam->vdev->lock = &cam->v4l2_lock; + v4l2_ctrl_handler_init(hdl, 12); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_BRIGHTNESS, + cam->params.pnp_id.device_type == DEVICE_STV_672 ? 1 : 0, + 255, 1, DEFAULT_BRIGHTNESS); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, DEFAULT_CONTRAST); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, DEFAULT_SATURATION); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_ACTIVE_MARKER, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT, 0, + V4L2_JPEG_ACTIVE_MARKER_DHT); + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, + 100, 1, 100); + cpia2_usb_alt.def = alternate; + cam->usb_alt = v4l2_ctrl_new_custom(hdl, &cpia2_usb_alt, NULL); + /* VP5 Only */ + if (cam->params.pnp_id.device_type != DEVICE_STV_672) + v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + /* Flicker control only valid for 672 */ + if (cam->params.pnp_id.device_type == DEVICE_STV_672) + v4l2_ctrl_new_std_menu(hdl, &cpia2_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, 0); + /* Light control only valid for the QX5 Microscope */ + if (cam->params.pnp_id.product == 0x151) { + cam->top_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + cam->bottom_light = v4l2_ctrl_new_std(hdl, &cpia2_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &cam->top_light); + } + + if (hdl->error) { + ret = hdl->error; + v4l2_ctrl_handler_free(hdl); + return ret; + } + + cam->vdev = cpia2_template; + video_set_drvdata(&cam->vdev, cam); + cam->vdev.lock = &cam->v4l2_lock; + cam->vdev.ctrl_handler = hdl; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &cam->vdev.flags); reset_camera_struct_v4l(cam); /* register v4l device */ - if (video_register_device(cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { ERR("video_register_device failed\n"); - video_device_release(cam->vdev); return -ENODEV; } @@ -1481,13 +1170,7 @@ int cpia2_register_camera(struct camera_data *cam) *****************************************************************************/ void cpia2_unregister_camera(struct camera_data *cam) { - if (!cam->open_count) { - video_unregister_device(cam->vdev); - } else { - LOG("%s removed while open, deferring " - "video_unregister_device\n", - video_device_node_name(cam->vdev)); - } + video_unregister_device(&cam->vdev); } /****************************************************************************** @@ -1524,23 +1207,12 @@ static void __init check_parameters(void) LOG("alternate specified is invalid, using %d\n", alternate); } - if (flicker_mode != NEVER_FLICKER && flicker_mode != ANTI_FLICKER_ON) { - flicker_mode = NEVER_FLICKER; + if (flicker_mode != 0 && flicker_mode != FLICKER_50 && flicker_mode != FLICKER_60) { + flicker_mode = 0; LOG("Flicker mode specified is invalid, using %d\n", flicker_mode); } - if (flicker_freq != FLICKER_50 && flicker_freq != FLICKER_60) { - flicker_freq = FLICKER_60; - LOG("Flicker mode specified is invalid, using %d\n", - flicker_freq); - } - - if(video_nr < -1 || video_nr > 64) { - video_nr = -1; - LOG("invalid video_nr specified, must be -1 to 64\n"); - } - DBG("Using %d buffers, each %d bytes, alternate=%d\n", num_buffers, buffer_size, alternate); } diff --git a/drivers/media/video/cpia2/cpia2dev.h b/drivers/media/video/cpia2/cpia2dev.h deleted file mode 100644 index f66691fe5a35..000000000000 --- a/drivers/media/video/cpia2/cpia2dev.h +++ /dev/null @@ -1,50 +0,0 @@ -/**************************************************************************** - * - * Filename: cpia2dev.h - * - * Copyright 2001, STMicrolectronics, Inc. - * - * Contact: steve.miller@st.com - * - * Description: - * This file provides definitions for applications wanting to use the - * cpia2 driver beyond the generic v4l capabilities. - * - * 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 CPIA2_DEV_HEADER -#define CPIA2_DEV_HEADER - -#include - -/*** - * The following defines are ioctl numbers based on video4linux private ioctls, - * which can range from 192 (BASE_VIDIOCPRIVATE) to 255. All of these take int - * args - */ -#define CPIA2_IOC_SET_GPIO _IOW('v', BASE_VIDIOC_PRIVATE + 17, __u32) - -/* V4L2 driver specific controls */ -#define CPIA2_CID_TARGET_KB (V4L2_CID_PRIVATE_BASE+0) -#define CPIA2_CID_GPIO (V4L2_CID_PRIVATE_BASE+1) -#define CPIA2_CID_FLICKER_MODE (V4L2_CID_PRIVATE_BASE+2) -#define CPIA2_CID_FRAMERATE (V4L2_CID_PRIVATE_BASE+3) -#define CPIA2_CID_USB_ALT (V4L2_CID_PRIVATE_BASE+4) -#define CPIA2_CID_LIGHTS (V4L2_CID_PRIVATE_BASE+5) -#define CPIA2_CID_RESET_CAMERA (V4L2_CID_PRIVATE_BASE+6) - -#endif diff --git a/drivers/media/video/cx18/cx18-alsa-main.c b/drivers/media/video/cx18/cx18-alsa-main.c index e118361c2e7b..6d2a98246b6d 100644 --- a/drivers/media/video/cx18/cx18-alsa-main.c +++ b/drivers/media/video/cx18/cx18-alsa-main.c @@ -285,6 +285,7 @@ static void __exit cx18_alsa_exit(void) drv = driver_find("cx18", &pci_bus_type); ret = driver_for_each_device(drv, NULL, NULL, cx18_alsa_exit_callback); + (void)ret; /* suppress compiler warning */ cx18_ext_init = NULL; printk(KERN_INFO "cx18-alsa: module unload complete\n"); diff --git a/drivers/media/video/cx18/cx18-alsa-pcm.c b/drivers/media/video/cx18/cx18-alsa-pcm.c index 82d195be9197..7a5b84a86bb3 100644 --- a/drivers/media/video/cx18/cx18-alsa-pcm.c +++ b/drivers/media/video/cx18/cx18-alsa-pcm.c @@ -190,7 +190,7 @@ static int snd_cx18_pcm_capture_open(struct snd_pcm_substream *substream) ret = cx18_start_v4l2_encode_stream(s); snd_cx18_unlock(cxsc); - return 0; + return ret; } static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) @@ -199,12 +199,11 @@ static int snd_cx18_pcm_capture_close(struct snd_pcm_substream *substream) struct v4l2_device *v4l2_dev = cxsc->v4l2_dev; struct cx18 *cx = to_cx18(v4l2_dev); struct cx18_stream *s; - int ret; /* Instruct the cx18 to stop sending packets */ snd_cx18_lock(cxsc); s = &cx->streams[CX18_ENC_STREAM_TYPE_PCM]; - ret = cx18_stop_v4l2_encode_stream(s, 0); + cx18_stop_v4l2_encode_stream(s, 0); clear_bit(CX18_F_S_STREAMING, &s->s_flags); cx18_release_stream(s); @@ -252,13 +251,10 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs, static int snd_cx18_pcm_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { - int ret; - dprintk("%s called\n", __func__); - ret = snd_pcm_alloc_vmalloc_buffer(substream, + return snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(params)); - return 0; } static int snd_cx18_pcm_hw_free(struct snd_pcm_substream *substream) diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c index be49f68ddf37..35fde4e931f5 100644 --- a/drivers/media/video/cx18/cx18-ioctl.c +++ b/drivers/media/video/cx18/cx18-ioctl.c @@ -1137,7 +1137,7 @@ static long cx18_default(struct file *file, void *fh, bool valid_prio, } default: - return -EINVAL; + return -ENOTTY; } return 0; } diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c index 0c7796e76ac0..ed8118390b02 100644 --- a/drivers/media/video/cx18/cx18-mailbox.c +++ b/drivers/media/video/cx18/cx18-mailbox.c @@ -595,9 +595,8 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu) static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) { const struct cx18_api_info *info = find_api_info(cmd); - u32 state, irq, req, ack, err; + u32 irq, req, ack, err; struct cx18_mailbox __iomem *mb; - u32 __iomem *xpu_state; wait_queue_head_t *waitq; struct mutex *mb_lock; unsigned long int t0, timeout, ret; @@ -628,14 +627,12 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) mb_lock = &cx->epu2apu_mb_lock; irq = IRQ_EPU_TO_APU; mb = &cx->scb->epu2apu_mb; - xpu_state = &cx->scb->apu_state; break; case CPU: waitq = &cx->mb_cpu_waitq; mb_lock = &cx->epu2cpu_mb_lock; irq = IRQ_EPU_TO_CPU; mb = &cx->scb->epu2cpu_mb; - xpu_state = &cx->scb->cpu_state; break; default: CX18_WARN("Unknown RPU (%d) for API call\n", info->rpu); @@ -653,7 +650,6 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[]) * by a signal, we may get here and find a busy mailbox. After waiting, * mark it "not busy" from our end, if the XPU hasn't ack'ed it still. */ - state = cx18_readl(cx, xpu_state); req = cx18_readl(cx, &mb->request); timeout = msecs_to_jiffies(10); ret = wait_event_timeout(*waitq, diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c index 638cca156b58..4185bcb80ca3 100644 --- a/drivers/media/video/cx18/cx18-streams.c +++ b/drivers/media/video/cx18/cx18-streams.c @@ -980,7 +980,6 @@ void cx18_stop_all_captures(struct cx18 *cx) int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) { struct cx18 *cx = s->cx; - unsigned long then; if (!cx18_stream_enabled(s)) return -EINVAL; @@ -999,8 +998,6 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end) else cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle); - then = jiffies; - if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) { CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n"); } diff --git a/drivers/media/video/cx231xx/cx231xx-417.c b/drivers/media/video/cx231xx/cx231xx-417.c index d4327dab5a36..ce2f62238a19 100644 --- a/drivers/media/video/cx231xx/cx231xx-417.c +++ b/drivers/media/video/cx231xx/cx231xx-417.c @@ -1095,7 +1095,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) { int version; int retval; - u32 i, data[7]; + u32 i; u32 val = 0; dprintk(1, "%s()\n", __func__); @@ -1154,6 +1154,11 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) CX231xx_CUSTOM_EXTENSION_USR_DATA, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); */ + +#if 0 + /* TODO */ + u32 data[7]; + /* Setup to capture VBI */ data[0] = 0x0001BD00; data[1] = 1; /* frames per interrupt */ @@ -1162,7 +1167,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) data[4] = 0x206080C0; /* stop codes */ data[5] = 6; /* lines */ data[6] = 64; /* BPL */ -/* + cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_CONFIG, 7, 0, data[0], data[1], data[2], data[3], data[4], data[5], data[6]); @@ -1175,7 +1180,7 @@ static int cx231xx_initialize_codec(struct cx231xx *dev) cx231xx_api_cmd(dev, CX2341X_ENC_SET_VBI_LINE, 5, 0, i | 0x80000000, valid, 0, 0, 0); } -*/ +#endif /* cx231xx_api_cmd(dev, CX2341X_ENC_MUTE_AUDIO, 1, 0, CX231xx_UNMUTE); msleep(60); */ @@ -1792,17 +1797,16 @@ static int vidioc_streamon(struct file *file, void *priv, struct cx231xx_fh *fh = file->private_data; struct cx231xx *dev = fh->dev; - int rc = 0; dprintk(3, "enter vidioc_streamon()\n"); cx231xx_set_alt_setting(dev, INDEX_TS1, 0); - rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); + cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE); if (dev->USE_ISO) - rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, + cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS, CX231XX_NUM_BUFS, dev->video_mode.max_pkt_size, cx231xx_isoc_copy); else { - rc = cx231xx_init_bulk(dev, 320, + cx231xx_init_bulk(dev, 320, 5, dev->ts1_mode.max_pkt_size, cx231xx_bulk_copy); diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c index a2c2b7d343ec..068f78dc5d13 100644 --- a/drivers/media/video/cx231xx/cx231xx-audio.c +++ b/drivers/media/video/cx231xx/cx231xx-audio.c @@ -523,21 +523,24 @@ static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream) static int snd_cx231xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - unsigned int channels, rate, format; int ret; dprintk("Setting capture parameters\n"); ret = snd_pcm_alloc_vmalloc_buffer(substream, params_buffer_bytes(hw_params)); - format = params_format(hw_params); - rate = params_rate(hw_params); - channels = params_channels(hw_params); - +#if 0 /* TODO: set up cx231xx audio chip to deliver the correct audio format, current default is 48000hz multiplexed => 96000hz mono which shouldn't matter since analogue TV only supports mono */ - return 0; + unsigned int channels, rate, format; + + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); +#endif + + return ret; } static int snd_cx231xx_hw_capture_free(struct snd_pcm_substream *substream) @@ -586,7 +589,7 @@ static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, int cmd) { struct cx231xx *dev = snd_pcm_substream_chip(substream); - int retval; + int retval = 0; if (dev->state & DEV_DISCONNECTED) return -ENODEV; @@ -601,12 +604,13 @@ static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream, break; default: retval = -EINVAL; + break; } spin_unlock(&dev->adev.slock); schedule_work(&dev->wq_trigger); - return 0; + return retval; } static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c index 53ff26e7abf7..b085a3c6dc04 100644 --- a/drivers/media/video/cx231xx/cx231xx-avcore.c +++ b/drivers/media/video/cx231xx/cx231xx-avcore.c @@ -934,33 +934,29 @@ int cx231xx_set_decoder_video_input(struct cx231xx *dev, void cx231xx_enable656(struct cx231xx *dev) { u8 temp = 0; - int status; /*enable TS1 data[0:7] as output to export 656*/ - status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF); + vid_blk_write_byte(dev, TS1_PIN_CTL0, 0xFF); /*enable TS1 clock as output to export 656*/ - status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); + vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); temp = temp|0x04; - status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); - + vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); } EXPORT_SYMBOL_GPL(cx231xx_enable656); void cx231xx_disable656(struct cx231xx *dev) { u8 temp = 0; - int status; + vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00); - status = vid_blk_write_byte(dev, TS1_PIN_CTL0, 0x00); - - status = vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); + vid_blk_read_byte(dev, TS1_PIN_CTL1, &temp); temp = temp&0xFB; - status = vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); + vid_blk_write_byte(dev, TS1_PIN_CTL1, temp); } EXPORT_SYMBOL_GPL(cx231xx_disable656); @@ -1320,117 +1316,115 @@ void update_HH_register_after_set_DIF(struct cx231xx *dev) void cx231xx_dump_HH_reg(struct cx231xx *dev) { - u8 status = 0; u32 value = 0; u16 i = 0; value = 0x45005390; - status = vid_blk_write_word(dev, 0x104, value); + vid_blk_write_word(dev, 0x104, value); for (i = 0x100; i < 0x140; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } for (i = 0x300; i < 0x400; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } for (i = 0x400; i < 0x440; i++) { - status = vid_blk_read_word(dev, i, &value); + vid_blk_read_word(dev, i, &value); cx231xx_info("reg0x%x=0x%x\n", i, value); i = i+3; } - status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); vid_blk_write_word(dev, AFE_CTRL_C2HH_SRC_CTRL, 0x4485D390); - status = vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); + vid_blk_read_word(dev, AFE_CTRL_C2HH_SRC_CTRL, &value); cx231xx_info("AFE_CTRL_C2HH_SRC_CTRL=0x%x\n", value); } void cx231xx_dump_SC_reg(struct cx231xx *dev) { u8 value[4] = { 0, 0, 0, 0 }; - int status = 0; cx231xx_info("cx231xx_dump_SC_reg!\n"); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", BOARD_CFG_STAT, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS_MODE_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS_MODE_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_CFG_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_CFG_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS1_LENGTH_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS1_LENGTH_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_CFG_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_CFG_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, TS2_LENGTH_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", TS2_LENGTH_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", EP_MODE_SET, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_PTN3, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_PTN3, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK0, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK0, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_PWR_MASK2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_PWR_MASK2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_GAIN, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_GAIN, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_CAR_REG, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_CAR_REG, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG1, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG1, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, CIR_OT_CFG2, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", CIR_OT_CFG2, value[0], value[1], value[2], value[3]); - status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, + cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value, 4); cx231xx_info("reg0x%x=0x%x 0x%x 0x%x 0x%x\n", PWR_CTL_EN, value[0], value[1], value[2], value[3]); @@ -1441,18 +1435,15 @@ void cx231xx_dump_SC_reg(struct cx231xx *dev) void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev) { - u8 status = 0; u8 value = 0; - - - status = afe_read_byte(dev, ADC_STATUS2_CH3, &value); + afe_read_byte(dev, ADC_STATUS2_CH3, &value); value = (value & 0xFE)|0x01; - status = afe_write_byte(dev, ADC_STATUS2_CH3, value); + afe_write_byte(dev, ADC_STATUS2_CH3, value); - status = afe_read_byte(dev, ADC_STATUS2_CH3, &value); + afe_read_byte(dev, ADC_STATUS2_CH3, &value); value = (value & 0xFE)|0x00; - status = afe_write_byte(dev, ADC_STATUS2_CH3, value); + afe_write_byte(dev, ADC_STATUS2_CH3, value); /* @@ -1464,44 +1455,43 @@ void cx231xx_Setup_AFE_for_LowIF(struct cx231xx *dev) for low-if agc defect */ - status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value); + afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3, &value); value = (value & 0xFC)|0x00; - status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value); + afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, value); - status = afe_read_byte(dev, ADC_INPUT_CH3, &value); + afe_read_byte(dev, ADC_INPUT_CH3, &value); value = (value & 0xF9)|0x02; - status = afe_write_byte(dev, ADC_INPUT_CH3, value); + afe_write_byte(dev, ADC_INPUT_CH3, value); - status = afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value); + afe_read_byte(dev, ADC_FB_FRCRST_CH3, &value); value = (value & 0xFB)|0x04; - status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, value); + afe_write_byte(dev, ADC_FB_FRCRST_CH3, value); - status = afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value); + afe_read_byte(dev, ADC_DCSERVO_DEM_CH3, &value); value = (value & 0xFC)|0x03; - status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value); + afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC1_CH3, &value); value = (value & 0xFB)|0x04; - status = afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC1_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); value = (value & 0xF8)|0x06; - status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); - status = afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); + afe_read_byte(dev, ADC_CTRL_DAC23_CH3, &value); value = (value & 0x8F)|0x40; - status = afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); + afe_write_byte(dev, ADC_CTRL_DAC23_CH3, value); - status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value); + afe_read_byte(dev, ADC_PWRDN_CLAMP_CH3, &value); value = (value & 0xDF)|0x20; - status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value); + afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, value); } void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, u8 spectral_invert, u32 mode) { u32 colibri_carrier_offset = 0; - u8 status = 0; u32 func_mode = 0x01; /* Device has a DIF if this function is called */ u32 standard = 0; u8 value[4] = { 0, 0, 0, 0 }; @@ -1511,15 +1501,15 @@ void cx231xx_set_Colibri_For_LowIF(struct cx231xx *dev, u32 if_freq, value[1] = (u8) 0x6F; value[2] = (u8) 0x6F; value[3] = (u8) 0x6F; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN, value, 4); /*Set colibri for low IF*/ - status = cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF); + cx231xx_afe_set_mode(dev, AFE_MODE_LOW_IF); /* Set C2HH for low IF operation.*/ standard = dev->norm; - status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, + cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode, func_mode, standard); /* Get colibri offsets.*/ @@ -1556,7 +1546,6 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, u8 spectral_invert, u32 mode) { unsigned long pll_freq_word; - int status = 0; u32 dif_misc_ctrl_value = 0; u64 pll_freq_u64 = 0; u32 i = 0; @@ -1567,7 +1556,7 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, if (mode == TUNER_MODE_FM_RADIO) { pll_freq_word = 0x905A1CAC; - status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); + vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); } else /*KSPROPERTY_TUNER_MODE_TV*/{ /* Calculate the PLL frequency word based on the adjusted if_freq*/ @@ -1576,23 +1565,23 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, do_div(pll_freq_u64, 50000000); pll_freq_word = (u32)pll_freq_u64; /*pll_freq_word = 0x3463497;*/ - status = vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); + vid_blk_write_word(dev, DIF_PLL_FREQ_WORD, pll_freq_word); if (spectral_invert) { if_freq -= 400000; /* Enable Spectral Invert*/ - status = vid_blk_read_word(dev, DIF_MISC_CTRL, + vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); dif_misc_ctrl_value = dif_misc_ctrl_value | 0x00200000; - status = vid_blk_write_word(dev, DIF_MISC_CTRL, + vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); } else { if_freq += 400000; /* Disable Spectral Invert*/ - status = vid_blk_read_word(dev, DIF_MISC_CTRL, + vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value); dif_misc_ctrl_value = dif_misc_ctrl_value & 0xFFDFFFFF; - status = vid_blk_write_word(dev, DIF_MISC_CTRL, + vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value); } @@ -1606,10 +1595,10 @@ void cx231xx_set_DIF_bandpass(struct cx231xx *dev, u32 if_freq, } cx231xx_info("Enter IF=%zd\n", - sizeof(Dif_set_array)/sizeof(struct dif_settings)); - for (i = 0; i < sizeof(Dif_set_array)/sizeof(struct dif_settings); i++) { + ARRAY_SIZE(Dif_set_array)); + for (i = 0; i < ARRAY_SIZE(Dif_set_array); i++) { if (Dif_set_array[i].if_freq == if_freq) { - status = vid_blk_write_word(dev, + vid_blk_write_word(dev, Dif_set_array[i].register_address, Dif_set_array[i].value); } } @@ -3090,31 +3079,30 @@ int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) */ int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len) { - int status = 0; int i = 0; /* get the lock */ mutex_lock(&dev->gpio_i2c_lock); /* start */ - status = cx231xx_gpio_i2c_start(dev); + cx231xx_gpio_i2c_start(dev); /* write dev_addr */ - status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); + cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1); /* read Ack */ - status = cx231xx_gpio_i2c_read_ack(dev); + cx231xx_gpio_i2c_read_ack(dev); for (i = 0; i < len; i++) { /* Write data */ - status = cx231xx_gpio_i2c_write_byte(dev, buf[i]); + cx231xx_gpio_i2c_write_byte(dev, buf[i]); /* read Ack */ - status = cx231xx_gpio_i2c_read_ack(dev); + cx231xx_gpio_i2c_read_ack(dev); } /* write End */ - status = cx231xx_gpio_i2c_end(dev); + cx231xx_gpio_i2c_end(dev); /* release the lock */ mutex_unlock(&dev->gpio_i2c_lock); diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c index 08dd930f882a..05358d486135 100644 --- a/drivers/media/video/cx231xx/cx231xx-core.c +++ b/drivers/media/video/cx231xx/cx231xx-core.c @@ -754,7 +754,7 @@ int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode) } } - return 0; + return errCode ? -EINVAL : 0; } EXPORT_SYMBOL_GPL(cx231xx_set_mode); @@ -764,7 +764,7 @@ int cx231xx_ep5_bulkout(struct cx231xx *dev, u8 *firmware, u16 size) int actlen, ret = -ENOMEM; u32 *buffer; -buffer = kzalloc(4096, GFP_KERNEL); + buffer = kzalloc(4096, GFP_KERNEL); if (buffer == NULL) { cx231xx_info("out of mem\n"); return -ENOMEM; @@ -772,16 +772,16 @@ buffer = kzalloc(4096, GFP_KERNEL); memcpy(&buffer[0], firmware, 4096); ret = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 5), - buffer, 4096, &actlen, 2000); + buffer, 4096, &actlen, 2000); if (ret) cx231xx_info("bulk message failed: %d (%d/%d)", ret, - size, actlen); + size, actlen); else { errCode = actlen != size ? -1 : 0; } -kfree(buffer); - return 0; + kfree(buffer); + return errCode; } /***************************************************************** @@ -797,7 +797,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - int rc, i; + int i; switch (urb->status) { case 0: /* success */ @@ -814,7 +814,7 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->video_mode.slock); - rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb); + dev->video_mode.isoc_ctl.isoc_copy(dev, urb); spin_unlock(&dev->video_mode.slock); /* Reset urb buffers */ @@ -822,7 +822,6 @@ static void cx231xx_isoc_irq_callback(struct urb *urb) urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } - urb->status = 0; urb->status = usb_submit_urb(urb, GFP_ATOMIC); if (urb->status) { @@ -843,7 +842,6 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode); - int rc; switch (urb->status) { case 0: /* success */ @@ -860,12 +858,10 @@ static void cx231xx_bulk_irq_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->video_mode.slock); - rc = dev->video_mode.bulk_ctl.bulk_copy(dev, urb); + dev->video_mode.bulk_ctl.bulk_copy(dev, urb); spin_unlock(&dev->video_mode.slock); /* Reset urb buffers */ - urb->status = 0; - urb->status = usb_submit_urb(urb, GFP_ATOMIC); if (urb->status) { cx231xx_isocdbg("urb resubmit failed (error=%i)\n", @@ -1231,42 +1227,40 @@ int cx231xx_init_bulk(struct cx231xx *dev, int max_packets, EXPORT_SYMBOL_GPL(cx231xx_init_bulk); void cx231xx_stop_TS1(struct cx231xx *dev) { - int status = 0; u8 val[4] = { 0, 0, 0, 0 }; - val[0] = 0x00; - val[1] = 0x03; - val[2] = 0x00; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS_MODE_REG, val, 4); + val[0] = 0x00; + val[1] = 0x03; + val[2] = 0x00; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS_MODE_REG, val, 4); - val[0] = 0x00; - val[1] = 0x70; - val[2] = 0x04; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); + val[0] = 0x00; + val[1] = 0x70; + val[2] = 0x04; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); } /* EXPORT_SYMBOL_GPL(cx231xx_stop_TS1); */ void cx231xx_start_TS1(struct cx231xx *dev) { - int status = 0; u8 val[4] = { 0, 0, 0, 0 }; - val[0] = 0x03; - val[1] = 0x03; - val[2] = 0x00; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS_MODE_REG, val, 4); + val[0] = 0x03; + val[1] = 0x03; + val[2] = 0x00; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS_MODE_REG, val, 4); - val[0] = 0x04; - val[1] = 0xA3; - val[2] = 0x3B; - val[3] = 0x00; - status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, - TS1_CFG_REG, val, 4); + val[0] = 0x04; + val[1] = 0xA3; + val[2] = 0x3B; + val[3] = 0x00; + cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, + TS1_CFG_REG, val, 4); } /* EXPORT_SYMBOL_GPL(cx231xx_start_TS1); */ /***************************************************************** diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c index 8cdee5f78f13..3d15314e1f88 100644 --- a/drivers/media/video/cx231xx/cx231xx-vbi.c +++ b/drivers/media/video/cx231xx/cx231xx-vbi.c @@ -83,7 +83,6 @@ static inline void print_err_status(struct cx231xx *dev, int packet, int status) */ static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; int rc = 1; unsigned char *p_buffer; @@ -102,8 +101,6 @@ static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->vbi_mode.bulk_ctl.buf; - /* get buffer pointer and length */ p_buffer = urb->transfer_buffer; buffer_size = urb->actual_length; @@ -310,7 +307,6 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) struct cx231xx_video_mode *vmode = container_of(dma_q, struct cx231xx_video_mode, vidq); struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode); - int rc; switch (urb->status) { case 0: /* success */ @@ -328,7 +324,7 @@ static void cx231xx_irq_vbi_callback(struct urb *urb) /* Copy data from URB */ spin_lock(&dev->vbi_mode.slock); - rc = dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); + dev->vbi_mode.bulk_ctl.bulk_copy(dev, urb); spin_unlock(&dev->vbi_mode.slock); /* Reset status */ diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c index 7f916f0685e9..523aa49d6b86 100644 --- a/drivers/media/video/cx231xx/cx231xx-video.c +++ b/drivers/media/video/cx231xx/cx231xx-video.c @@ -326,9 +326,7 @@ static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q, */ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; - unsigned char *outp = NULL; int i, rc = 1; unsigned char *p_buffer; u32 bytes_parsed = 0, buffer_size = 0; @@ -346,10 +344,6 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->video_mode.isoc_ctl.buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - for (i = 0; i < urb->number_of_packets; i++) { int status = urb->iso_frame_desc[i].status; @@ -429,9 +423,7 @@ static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb) static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) { - struct cx231xx_buffer *buf; struct cx231xx_dmaqueue *dma_q = urb->context; - unsigned char *outp = NULL; int rc = 1; unsigned char *p_buffer; u32 bytes_parsed = 0, buffer_size = 0; @@ -449,10 +441,6 @@ static inline int cx231xx_bulk_copy(struct cx231xx *dev, struct urb *urb) return 0; } - buf = dev->video_mode.bulk_ctl.buf; - if (buf != NULL) - outp = videobuf_to_vmalloc(&buf->vb); - if (1) { /* get buffer pointer and length */ @@ -701,13 +689,9 @@ void cx231xx_reset_video_buffer(struct cx231xx *dev, buf = dev->video_mode.bulk_ctl.buf; if (buf == NULL) { - u8 *outp = NULL; /* first try to get the buffer */ get_next_buf(dma_q, &buf); - if (buf) - outp = videobuf_to_vmalloc(&buf->vb); - dma_q->pos = 0; dma_q->field1_done = 0; dma_q->current_field = -1; @@ -2561,6 +2545,10 @@ static struct video_device *cx231xx_vdev_init(struct cx231xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c index 19b5499d2624..13739e002a63 100644 --- a/drivers/media/video/cx23885/cx23885-cards.c +++ b/drivers/media/video/cx23885/cx23885-cards.c @@ -497,6 +497,10 @@ struct cx23885_board cx23885_boards[] = { .name = "TerraTec Cinergy T PCIe Dual", .portb = CX23885_MPEG_DVB, .portc = CX23885_MPEG_DVB, + }, + [CX23885_BOARD_TEVII_S471] = { + .name = "TeVii S471", + .portb = CX23885_MPEG_DVB, } }; const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards); @@ -705,6 +709,10 @@ struct cx23885_subid cx23885_subids[] = { .subvendor = 0x153b, .subdevice = 0x117e, .card = CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL, + }, { + .subvendor = 0xd471, + .subdevice = 0x9022, + .card = CX23885_BOARD_TEVII_S471, }, }; const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids); @@ -1460,6 +1468,7 @@ void cx23885_card_setup(struct cx23885_dev *dev) ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO; break; case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_TEVII_S471: case CX23885_BOARD_DVBWORLD_2005: ts1->gen_ctrl_val = 0x5; /* Parallel */ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */ diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c index 6ad227029a0f..697728f09430 100644 --- a/drivers/media/video/cx23885/cx23885-core.c +++ b/drivers/media/video/cx23885/cx23885-core.c @@ -1046,6 +1046,13 @@ static int cx23885_dev_setup(struct cx23885_dev *dev) if (cx23885_boards[dev->board].ci_type > 0) cx_clear(RDR_RDRCTL1, 1 << 8); + switch (dev->board) { + case CX23885_BOARD_TEVII_S470: + case CX23885_BOARD_TEVII_S471: + cx_clear(RDR_RDRCTL1, 1 << 8); + break; + } + return 0; } diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c index 6835eb1fc093..a80a92c47455 100644 --- a/drivers/media/video/cx23885/cx23885-dvb.c +++ b/drivers/media/video/cx23885/cx23885-dvb.c @@ -1173,6 +1173,13 @@ static int dvb_register(struct cx23885_tsport *port) break; } break; + case CX23885_BOARD_TEVII_S471: + i2c_bus = &dev->i2c_bus[1]; + + fe0->dvb.frontend = dvb_attach(ds3000_attach, + &tevii_ds3000_config, + &i2c_bus->i2c_adap); + break; default: printk(KERN_INFO "%s: The frontend of your DVB/ATSC card " " isn't supported yet\n", diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index f020f0568df4..d884784a1c85 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -89,6 +89,7 @@ #define CX23885_BOARD_MPX885 32 #define CX23885_BOARD_MYGICA_X8507 33 #define CX23885_BOARD_TERRATEC_CINERGY_T_PCIE_DUAL 34 +#define CX23885_BOARD_TEVII_S471 35 #define GPIO_0 0x00000001 #define GPIO_1 0x00000002 diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c index bb1ce346425d..c2bc39c58f82 100644 --- a/drivers/media/video/cx23885/cx23888-ir.c +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -331,9 +331,7 @@ static u64 ns_to_pulse_clocks(u32 ns) static u16 pulse_clocks_to_clock_divider(u64 count) { - u32 rem; - - rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + do_div(count, (FIFO_RXTX << 2) | 0x3); /* net result needs to be rounded down and decremented by 1 */ if (count > RXCLK_RCD + 1) diff --git a/drivers/media/video/cx25821/cx25821-alsa.c b/drivers/media/video/cx25821/cx25821-alsa.c index 03cfac476b03..1858a45dd081 100644 --- a/drivers/media/video/cx25821/cx25821-alsa.c +++ b/drivers/media/video/cx25821/cx25821-alsa.c @@ -290,11 +290,9 @@ static irqreturn_t cx25821_irq(int irq, void *dev_id) u32 status, pci_status; u32 audint_status, audint_mask; int loop, handled = 0; - int audint_count = 0; audint_status = cx_read(AUD_A_INT_STAT); audint_mask = cx_read(AUD_A_INT_MSK); - audint_count = cx_read(AUD_A_GPCNT); status = cx_read(PCI_INT_STAT); for (loop = 0; loop < 1; loop++) { diff --git a/drivers/media/video/cx25821/cx25821-audio-upstream.c b/drivers/media/video/cx25821/cx25821-audio-upstream.c index 20c7ca3351a8..8b2a99975c23 100644 --- a/drivers/media/video/cx25821/cx25821-audio-upstream.c +++ b/drivers/media/video/cx25821/cx25821-audio-upstream.c @@ -585,7 +585,7 @@ int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, audio_status; + u32 audio_status; int handled = 0; struct sram_channel *sram_ch; @@ -594,7 +594,6 @@ static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) sram_ch = dev->channels[dev->_audio_upstream_channel].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); audio_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-core.c b/drivers/media/video/cx25821/cx25821-core.c index 7930ca58349f..83c1aa6b2e6c 100644 --- a/drivers/media/video/cx25821/cx25821-core.c +++ b/drivers/media/video/cx25821/cx25821-core.c @@ -379,14 +379,6 @@ static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) return cx_read(bus->reg_stat) & 0x01; } -void cx_i2c_read_print(struct cx25821_dev *dev, u32 reg, const char *reg_string) -{ - int tmp = 0; - u32 value = 0; - - value = cx25821_i2c_read(&dev->i2c_bus[0], reg, &tmp); -} - static void cx25821_registers_init(struct cx25821_dev *dev) { u32 tmp; @@ -895,7 +887,7 @@ static void cx25821_iounmap(struct cx25821_dev *dev) static int cx25821_dev_setup(struct cx25821_dev *dev) { - int io_size = 0, i; + int i; pr_info("\n***********************************\n"); pr_info("cx25821 set up\n"); @@ -960,7 +952,6 @@ static int cx25821_dev_setup(struct cx25821_dev *dev) /* PCIe stuff */ dev->base_io_addr = pci_resource_start(dev->pci, 0); - io_size = pci_resource_len(dev->pci, 0); if (!dev->base_io_addr) { CX25821_ERR("No PCI Memory resources, exiting!\n"); @@ -1317,13 +1308,12 @@ void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) static irqreturn_t cx25821_irq(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 pci_status, pci_mask; + u32 pci_status; u32 vid_status; int i, handled = 0; u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; pci_status = cx_read(PCI_INT_STAT); - pci_mask = cx_read(PCI_INT_MSK); if (pci_status == 0) goto out; diff --git a/drivers/media/video/cx25821/cx25821-i2c.c b/drivers/media/video/cx25821/cx25821-i2c.c index 12d7300fa1e9..6311180f430c 100644 --- a/drivers/media/video/cx25821/cx25821-i2c.c +++ b/drivers/media/video/cx25821/cx25821-i2c.c @@ -361,7 +361,6 @@ void cx25821_av_clk(struct cx25821_dev *dev, int enable) int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) { struct i2c_client *client = &bus->i2c_client; - int retval = 0; int v = 0; u8 addr[2] = { 0, 0 }; u8 buf[4] = { 0, 0, 0, 0 }; @@ -385,7 +384,7 @@ int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) msgs[0].addr = 0x44; msgs[1].addr = 0x44; - retval = i2c_xfer(client->adapter, msgs, 2); + i2c_xfer(client->adapter, msgs, 2); v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; *value = v; diff --git a/drivers/media/video/cx25821/cx25821-medusa-video.c b/drivers/media/video/cx25821/cx25821-medusa-video.c index 298a68d98c2f..313fb20a0b47 100644 --- a/drivers/media/video/cx25821/cx25821-medusa-video.c +++ b/drivers/media/video/cx25821/cx25821-medusa-video.c @@ -35,7 +35,6 @@ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, int enable) { - int ret_val = 1; u32 value = 0; u32 tmp = 0; int out_ctrl = OUT_CTRL1; @@ -79,13 +78,13 @@ static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, value &= 0xFFFFFF7F; /* clear BLUE_FIELD_EN */ if (enable) value |= 0x00000080; /* set BLUE_FIELD_EN */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); + cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp); value &= 0xFFFFFF7F; if (enable) value |= 0x00000080; /* set BLUE_FIELD_EN */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); + cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); } static int medusa_initialize_ntsc(struct cx25821_dev *dev) @@ -431,7 +430,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, { int decoder = 0; int decoder_count = 0; - int ret_val = 0; u32 hscale = 0x0; u32 vscale = 0x0; const int MAX_WIDTH = 720; @@ -482,9 +480,9 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, for (; decoder < decoder_count; decoder++) { /* write scaling values for each decoder */ - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], + cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL + (0x200 * decoder), hscale); - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], + cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL + (0x200 * decoder), vscale); } @@ -494,7 +492,6 @@ void medusa_set_resolution(struct cx25821_dev *dev, int width, static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, int duration) { - int ret_val = 0; u32 fld_cnt = 0; u32 tmp = 0; u32 disp_cnt_reg = DISP_AB_CNT; @@ -537,7 +534,7 @@ static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, fld_cnt |= ((u32) duration) << 16; } - ret_val = cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); + cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); mutex_unlock(&dev->lock); } diff --git a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c index 5a157cf4a95e..c8c94fbf5d8d 100644 --- a/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c +++ b/drivers/media/video/cx25821/cx25821-video-upstream-ch2.c @@ -587,7 +587,7 @@ int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, vid_status; + u32 vid_status; int handled = 0; int channel_num = 0; struct sram_channel *sram_ch; @@ -598,7 +598,6 @@ static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) channel_num = VID_UPSTREAM_SRAM_CHANNEL_J; sram_ch = dev->channels[channel_num].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-video-upstream.c b/drivers/media/video/cx25821/cx25821-video-upstream.c index 21e7d657f049..52c13e0b6492 100644 --- a/drivers/media/video/cx25821/cx25821-video-upstream.c +++ b/drivers/media/video/cx25821/cx25821-video-upstream.c @@ -637,7 +637,7 @@ int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) { struct cx25821_dev *dev = dev_id; - u32 msk_stat, vid_status; + u32 vid_status; int handled = 0; int channel_num = 0; struct sram_channel *sram_ch; @@ -649,7 +649,6 @@ static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) sram_ch = dev->channels[channel_num].sram_channels; - msk_stat = cx_read(sram_ch->int_mstat); vid_status = cx_read(sram_ch->int_stat); /* Only deal with our interrupt */ diff --git a/drivers/media/video/cx25821/cx25821-video.c b/drivers/media/video/cx25821/cx25821-video.c index ffd8bc79c02e..b38d4379cc36 100644 --- a/drivers/media/video/cx25821/cx25821-video.c +++ b/drivers/media/video/cx25821/cx25821-video.c @@ -109,25 +109,6 @@ struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc) return NULL; } -void cx25821_dump_video_queue(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q) -{ - struct cx25821_buffer *buf; - struct list_head *item; - dprintk(1, "%s()\n", __func__); - - if (!list_empty(&q->active)) { - list_for_each(item, &q->active) - buf = list_entry(item, struct cx25821_buffer, vb.queue); - } - - if (!list_empty(&q->queued)) { - list_for_each(item, &q->queued) - buf = list_entry(item, struct cx25821_buffer, vb.queue); - } - -} - void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count) { @@ -557,7 +538,7 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, struct cx25821_buffer *buf = container_of(vb, struct cx25821_buffer, vb); int rc, init_buffer = 0; - u32 line0_offset, line1_offset; + u32 line0_offset; struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); int bpl_local = LINE_SIZE_D1; int channel_opened = fh->channel_id; @@ -639,7 +620,6 @@ int cx25821_buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, case V4L2_FIELD_INTERLACED: /* All other formats are top field first */ line0_offset = 0; - line1_offset = buf->bpl; dprintk(1, "top field first\n"); cx25821_risc_buffer(dev->pci, &buf->risc, @@ -1830,7 +1810,6 @@ static long video_ioctl_set(struct file *file, unsigned int cmd, int i = 0; int cif_enable = 0; int cif_width = 0; - u32 value = 0; data_from_user = (struct downstream_user_struct *)arg; @@ -1914,7 +1893,7 @@ static long video_ioctl_set(struct file *file, unsigned int cmd, cx_write(data_from_user->reg_address, data_from_user->reg_data); break; case MEDUSA_READ: - value = cx25821_i2c_read(&dev->i2c_bus[0], + cx25821_i2c_read(&dev->i2c_bus[0], (u16) data_from_user->reg_address, &data_from_user->reg_data); break; diff --git a/drivers/media/video/cx25821/cx25821-video.h b/drivers/media/video/cx25821/cx25821-video.h index d0d9538ca5b3..9652a5e35ba2 100644 --- a/drivers/media/video/cx25821/cx25821-video.h +++ b/drivers/media/video/cx25821/cx25821-video.h @@ -86,8 +86,6 @@ extern struct cx25821_fmt formats[]; extern struct cx25821_fmt *cx25821_format_by_fourcc(unsigned int fourcc); extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; -extern void cx25821_dump_video_queue(struct cx25821_dev *dev, - struct cx25821_dmaqueue *q); extern void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, u32 count); diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c index 13c380ebb562..38ce76ed1924 100644 --- a/drivers/media/video/cx25840/cx25840-ir.c +++ b/drivers/media/video/cx25840/cx25840-ir.c @@ -316,9 +316,7 @@ static u64 ns_to_pulse_clocks(u32 ns) static u16 pulse_clocks_to_clock_divider(u64 count) { - u32 rem; - - rem = do_div(count, (FIFO_RXTX << 2) | 0x3); + do_div(count, (FIFO_RXTX << 2) | 0x3); /* net result needs to be rounded down and decremented by 1 */ if (count > RXCLK_RCD + 1) @@ -860,12 +858,10 @@ static int cx25840_ir_tx_write(struct v4l2_subdev *sd, u8 *buf, size_t count, ssize_t *num) { struct cx25840_ir_state *ir_state = to_ir_state(sd); - struct i2c_client *c; if (ir_state == NULL) return -ENODEV; - c = ir_state->c; #if 0 /* * FIXME - the code below is an incomplete and untested sketch of what diff --git a/drivers/media/video/davinci/Kconfig b/drivers/media/video/davinci/Kconfig index 60a456ebdc7c..9337b5605c90 100644 --- a/drivers/media/video/davinci/Kconfig +++ b/drivers/media/video/davinci/Kconfig @@ -40,6 +40,7 @@ config VIDEO_VPSS_SYSTEM config VIDEO_VPFE_CAPTURE tristate "VPFE Video Capture Driver" depends on VIDEO_V4L2 && (ARCH_DAVINCI || ARCH_OMAP3) + depends on I2C select VIDEOBUF_DMA_CONTIG help Support for DMx/AMx VPFE based frame grabber. This is the diff --git a/drivers/media/video/davinci/vpbe_display.c b/drivers/media/video/davinci/vpbe_display.c index 1f3b1c729252..e106b72810a9 100644 --- a/drivers/media/video/davinci/vpbe_display.c +++ b/drivers/media/video/davinci/vpbe_display.c @@ -1618,6 +1618,10 @@ static __devinit int init_vpbe_layer(int i, struct vpbe_display *disp_dev, vbd->ioctl_ops = &vpbe_ioctl_ops; vbd->minor = -1; vbd->v4l2_dev = &disp_dev->vpbe_dev->v4l2_dev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vbd->flags); vbd->lock = &vpbe_display_layer->opslock; if (disp_dev->vpbe_dev->current_timings.timings_type & diff --git a/drivers/media/video/davinci/vpfe_capture.c b/drivers/media/video/davinci/vpfe_capture.c index 20cf271a774b..49a845fb804a 100644 --- a/drivers/media/video/davinci/vpfe_capture.c +++ b/drivers/media/video/davinci/vpfe_capture.c @@ -1761,7 +1761,7 @@ static long vpfe_param_handler(struct file *file, void *priv, } break; default: - ret = -EINVAL; + ret = -ENOTTY; } unlock_out: mutex_unlock(&vpfe_dev->lock); diff --git a/drivers/media/video/davinci/vpif_capture.c b/drivers/media/video/davinci/vpif_capture.c index 6504e40a31dd..96046957bf21 100644 --- a/drivers/media/video/davinci/vpif_capture.c +++ b/drivers/media/video/davinci/vpif_capture.c @@ -2228,6 +2228,10 @@ static __init int vpif_probe(struct platform_device *pdev) common = &(ch->common[VPIF_VIDEO_INDEX]); spin_lock_init(&common->irqlock); mutex_init(&common->lock); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags); ch->video_dev->lock = &common->lock; /* Initialize prio member of channel object */ v4l2_prio_init(&ch->prio); diff --git a/drivers/media/video/davinci/vpif_display.c b/drivers/media/video/davinci/vpif_display.c index 7fa34b4fae26..e6488ee7db18 100644 --- a/drivers/media/video/davinci/vpif_display.c +++ b/drivers/media/video/davinci/vpif_display.c @@ -1778,6 +1778,10 @@ static __init int vpif_probe(struct platform_device *pdev) v4l2_prio_init(&ch->prio); ch->common[VPIF_VIDEO_INDEX].fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &ch->video_dev->flags); ch->video_dev->lock = &common->lock; /* register video device */ diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index f6f622e123bd..928ef0d0429f 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -49,10 +49,10 @@ config VIDEO_EM28XX_DVB Empiatech em28xx chips. config VIDEO_EM28XX_RC - bool "EM28XX Remote Controller support" + tristate "EM28XX Remote Controller support" depends on RC_CORE depends on VIDEO_EM28XX depends on !(RC_CORE=m && VIDEO_EM28XX=y) - default y + default VIDEO_EM28XX ---help--- Enables Remote Controller support on em28xx driver. diff --git a/drivers/media/video/em28xx/Makefile b/drivers/media/video/em28xx/Makefile index 2abdf76c5203..c8b338d4be05 100644 --- a/drivers/media/video/em28xx/Makefile +++ b/drivers/media/video/em28xx/Makefile @@ -1,16 +1,15 @@ em28xx-y := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-y += em28xx-core.o em28xx-vbi.o -em28xx-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-input.o - em28xx-alsa-objs := em28xx-audio.o +em28xx-rc-objs := em28xx-input.o obj-$(CONFIG_VIDEO_EM28XX) += em28xx.o obj-$(CONFIG_VIDEO_EM28XX_ALSA) += em28xx-alsa.o obj-$(CONFIG_VIDEO_EM28XX_DVB) += em28xx-dvb.o +obj-$(CONFIG_VIDEO_EM28XX_RC) += em28xx-rc.o ccflags-y += -Idrivers/media/video ccflags-y += -Idrivers/media/common/tuners ccflags-y += -Idrivers/media/dvb/dvb-core ccflags-y += -Idrivers/media/dvb/frontends - diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c index e2a7b77c39c7..d7e2a3dc5525 100644 --- a/drivers/media/video/em28xx/em28xx-audio.c +++ b/drivers/media/video/em28xx/em28xx-audio.c @@ -343,7 +343,6 @@ static int snd_em28xx_pcm_close(struct snd_pcm_substream *substream) static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *hw_params) { - unsigned int channels, rate, format; int ret; dprintk("Setting capture parameters\n"); @@ -352,13 +351,17 @@ static int snd_em28xx_hw_capture_params(struct snd_pcm_substream *substream, params_buffer_bytes(hw_params)); if (ret < 0) return ret; - format = params_format(hw_params); - rate = params_rate(hw_params); - channels = params_channels(hw_params); - +#if 0 /* TODO: set up em28xx audio chip to deliver the correct audio format, current default is 48000hz multiplexed => 96000hz mono which shouldn't matter since analogue TV only supports mono */ + unsigned int channels, rate, format; + + format = params_format(hw_params); + rate = params_rate(hw_params); + channels = params_channels(hw_params); +#endif + return 0; } diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c index 9fd8cc7dbb23..20a7e24de6fb 100644 --- a/drivers/media/video/em28xx/em28xx-cards.c +++ b/drivers/media/video/em28xx/em28xx-cards.c @@ -69,6 +69,8 @@ struct em28xx_hash_table { unsigned int tuner; }; +static void em28xx_pre_card_setup(struct em28xx *dev); + /* * Reset sequences for analog/digital modes */ @@ -2361,7 +2363,7 @@ static int em28xx_hint_sensor(struct em28xx *dev) /* Since em28xx_pre_card_setup() requires a proper dev->model, * this won't work for boards with generic PCI IDs */ -void em28xx_pre_card_setup(struct em28xx *dev) +static void em28xx_pre_card_setup(struct em28xx *dev) { /* Set the initial XCLK and I2C clock values based on the board definition */ @@ -2661,55 +2663,7 @@ static int em28xx_hint_board(struct em28xx *dev) return -1; } -/* ----------------------------------------------------------------------- */ -void em28xx_register_i2c_ir(struct em28xx *dev) -{ - /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ - /* at address 0x18, so if that address is needed for another board in */ - /* the future, please put it after 0x1f. */ - struct i2c_board_info info; - const unsigned short addr_list[] = { - 0x1f, 0x30, 0x47, I2C_CLIENT_END - }; - - if (disable_ir) - return; - - memset(&info, 0, sizeof(struct i2c_board_info)); - memset(&dev->init_data, 0, sizeof(dev->init_data)); - strlcpy(info.type, "ir_video", I2C_NAME_SIZE); - - /* detect & configure */ - switch (dev->model) { - case EM2800_BOARD_TERRATEC_CINERGY_200: - case EM2820_BOARD_TERRATEC_CINERGY_250: - dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; - dev->init_data.get_key = em28xx_get_key_terratec; - dev->init_data.name = "i2c IR (EM28XX Terratec)"; - break; - case EM2820_BOARD_PINNACLE_USB_2: - dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; - dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; - dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; - break; - case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: - dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; - dev->init_data.get_key = em28xx_get_key_em_haup; - dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; - break; - case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: - dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; - dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; - dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; - break; - } - - if (dev->init_data.name) - info.platform_data = &dev->init_data; - i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); -} - -void em28xx_card_setup(struct em28xx *dev) +static void em28xx_card_setup(struct em28xx *dev) { /* * If the device can be a webcam, seek for a sensor. @@ -2849,13 +2803,6 @@ void em28xx_card_setup(struct em28xx *dev) break; } -#if defined(CONFIG_MODULES) && defined(MODULE) - if (dev->board.has_ir_i2c && !disable_ir) - request_module("ir-kbd-i2c"); -#endif - if (dev->board.has_snapshot_button) - em28xx_register_snapshot_button(dev); - if (dev->board.valid == EM28XX_BOARD_NOT_VALIDATED) { em28xx_errdev("\n\n"); em28xx_errdev("The support for this board weren't " @@ -2929,9 +2876,6 @@ void em28xx_card_setup(struct em28xx *dev) } em28xx_tuner_setup(dev); - - if(!disable_ir) - em28xx_ir_init(dev); } @@ -2948,6 +2892,8 @@ static void request_module_async(struct work_struct *work) if (dev->board.has_dvb) request_module("em28xx-dvb"); + if (dev->board.has_ir_i2c && !disable_ir) + request_module("em28xx-rc"); } static void request_modules(struct em28xx *dev) @@ -2972,12 +2918,6 @@ static void flush_request_modules(struct em28xx *dev) */ void em28xx_release_resources(struct em28xx *dev) { - if (dev->sbutton_input_dev) - em28xx_deregister_snapshot_button(dev); - - if (dev->ir) - em28xx_ir_fini(dev); - /*FIXME: I2C IR should be disconnected */ em28xx_release_analog_resources(dev); @@ -3005,9 +2945,6 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, dev->udev = udev; mutex_init(&dev->ctrl_urb_lock); spin_lock_init(&dev->slock); - init_waitqueue_head(&dev->open); - init_waitqueue_head(&dev->wait_frame); - init_waitqueue_head(&dev->wait_stream); dev->em28xx_write_regs = em28xx_write_regs; dev->em28xx_read_reg = em28xx_read_reg; @@ -3140,9 +3077,7 @@ static int em28xx_init_dev(struct em28xx *dev, struct usb_device *udev, /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); - INIT_LIST_HEAD(&dev->vidq.queued); INIT_LIST_HEAD(&dev->vbiq.active); - INIT_LIST_HEAD(&dev->vbiq.queued); if (dev->board.has_msp34xx) { /* Send a reset to other chips via gpio */ @@ -3447,8 +3382,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) resources */ mutex_lock(&dev->lock); - wake_up_interruptible_all(&dev->open); - v4l2_device_disconnect(&dev->v4l2_dev); if (dev->users) { @@ -3460,8 +3393,6 @@ static void em28xx_usb_disconnect(struct usb_interface *interface) dev->state |= DEV_MISCONFIGURED; em28xx_uninit_isoc(dev, dev->mode); dev->state |= DEV_DISCONNECTED; - wake_up_interruptible(&dev->wait_frame); - wake_up_interruptible(&dev->wait_stream); } else { dev->state |= DEV_DISCONNECTED; em28xx_release_resources(dev); diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c index 53a9fb91e97e..5717bdee8f1b 100644 --- a/drivers/media/video/em28xx/em28xx-core.c +++ b/drivers/media/video/em28xx/em28xx-core.c @@ -139,6 +139,7 @@ int em28xx_read_reg(struct em28xx *dev, u16 reg) { return em28xx_read_reg_req(dev, USB_REQ_GET_STATUS, reg); } +EXPORT_SYMBOL_GPL(em28xx_read_reg); /* * em28xx_write_regs_req() @@ -205,6 +206,7 @@ int em28xx_write_regs(struct em28xx *dev, u16 reg, char *buf, int len) return rc; } +EXPORT_SYMBOL_GPL(em28xx_write_regs); /* Write a single register */ int em28xx_write_reg(struct em28xx *dev, u16 reg, u8 val) @@ -239,6 +241,7 @@ int em28xx_write_reg_bits(struct em28xx *dev, u16 reg, u8 val, return em28xx_write_regs(dev, reg, &newval, 1); } +EXPORT_SYMBOL_GPL(em28xx_write_reg_bits); /* * em28xx_is_ac97_ready() @@ -666,7 +669,6 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } -EXPORT_SYMBOL_GPL(em28xx_capture_start); int em28xx_vbi_supported(struct em28xx *dev) { @@ -975,7 +977,6 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) else isoc_bufs = &dev->isoc_ctl.analog_bufs; - dev->isoc_ctl.nfields = -1; for (i = 0; i < isoc_bufs->num_bufs; i++) { urb = isoc_bufs->urb[i]; if (urb) { @@ -1007,6 +1008,31 @@ void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode) } EXPORT_SYMBOL_GPL(em28xx_uninit_isoc); +/* + * Stop URBs + */ +void em28xx_stop_urbs(struct em28xx *dev) +{ + int i; + struct urb *urb; + struct em28xx_usb_isoc_bufs *isoc_bufs = &dev->isoc_ctl.digital_bufs; + + em28xx_isocdbg("em28xx: called em28xx_stop_urbs\n"); + + for (i = 0; i < isoc_bufs->num_bufs; i++) { + urb = isoc_bufs->urb[i]; + if (urb) { + if (!irqs_disabled()) + usb_kill_urb(urb); + else + usb_unlink_urb(urb); + } + } + + em28xx_capture_start(dev, 0); +} +EXPORT_SYMBOL_GPL(em28xx_stop_urbs); + /* * Allocate URBs */ diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c index 503a8d5b5382..16410ac20092 100644 --- a/drivers/media/video/em28xx/em28xx-dvb.c +++ b/drivers/media/video/em28xx/em28xx-dvb.c @@ -183,7 +183,7 @@ static int em28xx_stop_streaming(struct em28xx_dvb *dvb) { struct em28xx *dev = dvb->adapter.priv; - em28xx_capture_start(dev, 0); + em28xx_stop_urbs(dev); em28xx_set_mode(dev, EM28XX_SUSPEND); @@ -336,6 +336,8 @@ struct drxk_config pctv_520e_drxk = { .single_master = 1, .microcode_name = "dvb-demod-drxk-pctv.fw", .chunk_size = 58, + .antenna_dvbt = true, /* disable LNA */ + .antenna_gpio = (1 << 2), /* disable LNA */ }; static int drxk_gate_ctrl(struct dvb_frontend *fe, int enable) @@ -474,8 +476,8 @@ static void terratec_h5_init(struct em28xx *dev) static void pctv_520e_init(struct em28xx *dev) { /* - * Init TDA8295(?) analog demodulator. Looks like I2C traffic to - * digital demodulator and tuner are routed via TDA8295. + * Init AVF4910B analog decoder. Looks like I2C traffic to + * digital demodulator and tuner are routed via AVF4910B. */ int i; struct { @@ -542,7 +544,8 @@ static struct cxd2820r_config em28xx_cxd2820r_config = { .i2c_address = (0xd8 >> 1), .ts_mode = CXD2820R_TS_SERIAL, - /* enable LNA for DVB-T2 and DVB-C */ + /* enable LNA for DVB-T, DVB-T2 and DVB-C */ + .gpio_dvbt[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, .gpio_dvbt2[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, .gpio_dvbc[0] = CXD2820R_GPIO_E | CXD2820R_GPIO_O | CXD2820R_GPIO_L, }; diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c index a88e169dba23..185db65b766e 100644 --- a/drivers/media/video/em28xx/em28xx-i2c.c +++ b/drivers/media/video/em28xx/em28xx-i2c.c @@ -553,9 +553,6 @@ int em28xx_i2c_register(struct em28xx *dev) if (i2c_scan) em28xx_do_i2c_scan(dev); - /* Instantiate the IR receiver device, if present */ - em28xx_register_i2c_ir(dev); - return 0; } diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c index 2630b265b0e8..fce5f7680c99 100644 --- a/drivers/media/video/em28xx/em28xx-input.c +++ b/drivers/media/video/em28xx/em28xx-input.c @@ -80,7 +80,7 @@ struct em28xx_IR { I2C IR based get keycodes - should be used with ir-kbd-i2c **********************************************************/ -int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char b; @@ -108,7 +108,7 @@ int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[2]; u16 code; @@ -157,7 +157,7 @@ int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) return 1; } -int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, +static int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) { unsigned char buf[3]; @@ -179,7 +179,8 @@ int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, return 1; } -int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw) +static int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, + u32 *ir_raw) { unsigned char subaddr, keydetect, key; @@ -387,7 +388,138 @@ int em28xx_ir_change_protocol(struct rc_dev *rc_dev, u64 rc_type) return rc; } -int em28xx_ir_init(struct em28xx *dev) +static void em28xx_register_i2c_ir(struct em28xx *dev) +{ + /* Leadtek winfast tv USBII deluxe can find a non working IR-device */ + /* at address 0x18, so if that address is needed for another board in */ + /* the future, please put it after 0x1f. */ + struct i2c_board_info info; + const unsigned short addr_list[] = { + 0x1f, 0x30, 0x47, I2C_CLIENT_END + }; + + memset(&info, 0, sizeof(struct i2c_board_info)); + memset(&dev->init_data, 0, sizeof(dev->init_data)); + strlcpy(info.type, "ir_video", I2C_NAME_SIZE); + + /* detect & configure */ + switch (dev->model) { + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: + dev->init_data.ir_codes = RC_MAP_EM_TERRATEC; + dev->init_data.get_key = em28xx_get_key_terratec; + dev->init_data.name = "i2c IR (EM28XX Terratec)"; + break; + case EM2820_BOARD_PINNACLE_USB_2: + dev->init_data.ir_codes = RC_MAP_PINNACLE_GREY; + dev->init_data.get_key = em28xx_get_key_pinnacle_usb_grey; + dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; + break; + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: + dev->init_data.ir_codes = RC_MAP_HAUPPAUGE; + dev->init_data.get_key = em28xx_get_key_em_haup; + dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; + break; + case EM2820_BOARD_LEADTEK_WINFAST_USBII_DELUXE: + dev->init_data.ir_codes = RC_MAP_WINFAST_USBII_DELUXE; + dev->init_data.get_key = em28xx_get_key_winfast_usbii_deluxe; + dev->init_data.name = "i2c IR (EM2820 Winfast TV USBII Deluxe)"; + break; + } + + if (dev->init_data.name) + info.platform_data = &dev->init_data; + i2c_new_probed_device(&dev->i2c_adap, &info, addr_list, NULL); +} + +/********************************************************** + Handle Webcam snapshot button + **********************************************************/ + +static void em28xx_query_sbutton(struct work_struct *work) +{ + /* Poll the register and see if the button is depressed */ + struct em28xx *dev = + container_of(work, struct em28xx, sbutton_query_work.work); + int ret; + + ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); + + if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { + u8 cleared; + /* Button is depressed, clear the register */ + cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; + em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); + + /* Not emulate the keypress */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 1); + /* Now unpress the key */ + input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, + 0); + } + + /* Schedule next poll */ + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); +} + +static void em28xx_register_snapshot_button(struct em28xx *dev) +{ + struct input_dev *input_dev; + int err; + + em28xx_info("Registering snapshot button...\n"); + input_dev = input_allocate_device(); + if (!input_dev) { + em28xx_errdev("input_allocate_device failed\n"); + return; + } + + usb_make_path(dev->udev, dev->snapshot_button_path, + sizeof(dev->snapshot_button_path)); + strlcat(dev->snapshot_button_path, "/sbutton", + sizeof(dev->snapshot_button_path)); + INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); + + input_dev->name = "em28xx snapshot button"; + input_dev->phys = dev->snapshot_button_path; + input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); + set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); + input_dev->keycodesize = 0; + input_dev->keycodemax = 0; + input_dev->id.bustype = BUS_USB; + input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); + input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); + input_dev->id.version = 1; + input_dev->dev.parent = &dev->udev->dev; + + err = input_register_device(input_dev); + if (err) { + em28xx_errdev("input_register_device failed\n"); + input_free_device(input_dev); + return; + } + + dev->sbutton_input_dev = input_dev; + schedule_delayed_work(&dev->sbutton_query_work, + msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return; + +} + +static void em28xx_deregister_snapshot_button(struct em28xx *dev) +{ + if (dev->sbutton_input_dev != NULL) { + em28xx_info("Deregistering snapshot button\n"); + cancel_delayed_work_sync(&dev->sbutton_query_work); + input_unregister_device(dev->sbutton_input_dev); + dev->sbutton_input_dev = NULL; + } + return; +} + +static int em28xx_ir_init(struct em28xx *dev) { struct em28xx_IR *ir; struct rc_dev *rc; @@ -448,6 +580,15 @@ int em28xx_ir_init(struct em28xx *dev) if (err) goto err_out_stop; + em28xx_register_i2c_ir(dev); + +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c) + request_module("ir-kbd-i2c"); +#endif + if (dev->board.has_snapshot_button) + em28xx_register_snapshot_button(dev); + return 0; err_out_stop: @@ -458,10 +599,12 @@ int em28xx_ir_init(struct em28xx *dev) return err; } -int em28xx_ir_fini(struct em28xx *dev) +static int em28xx_ir_fini(struct em28xx *dev) { struct em28xx_IR *ir = dev->ir; + em28xx_deregister_snapshot_button(dev); + /* skip detach on non attached boards */ if (!ir) return 0; @@ -475,89 +618,26 @@ int em28xx_ir_fini(struct em28xx *dev) return 0; } -/********************************************************** - Handle Webcam snapshot button - **********************************************************/ +static struct em28xx_ops rc_ops = { + .id = EM28XX_RC, + .name = "Em28xx Input Extension", + .init = em28xx_ir_init, + .fini = em28xx_ir_fini, +}; -static void em28xx_query_sbutton(struct work_struct *work) +static int __init em28xx_rc_register(void) { - /* Poll the register and see if the button is depressed */ - struct em28xx *dev = - container_of(work, struct em28xx, sbutton_query_work.work); - int ret; - - ret = em28xx_read_reg(dev, EM28XX_R0C_USBSUSP); - - if (ret & EM28XX_R0C_USBSUSP_SNAPSHOT) { - u8 cleared; - /* Button is depressed, clear the register */ - cleared = ((u8) ret) & ~EM28XX_R0C_USBSUSP_SNAPSHOT; - em28xx_write_regs(dev, EM28XX_R0C_USBSUSP, &cleared, 1); - - /* Not emulate the keypress */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 1); - /* Now unpress the key */ - input_report_key(dev->sbutton_input_dev, EM28XX_SNAPSHOT_KEY, - 0); - } - - /* Schedule next poll */ - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); + return em28xx_register_extension(&rc_ops); } -void em28xx_register_snapshot_button(struct em28xx *dev) +static void __exit em28xx_rc_unregister(void) { - struct input_dev *input_dev; - int err; - - em28xx_info("Registering snapshot button...\n"); - input_dev = input_allocate_device(); - if (!input_dev) { - em28xx_errdev("input_allocate_device failed\n"); - return; - } - - usb_make_path(dev->udev, dev->snapshot_button_path, - sizeof(dev->snapshot_button_path)); - strlcat(dev->snapshot_button_path, "/sbutton", - sizeof(dev->snapshot_button_path)); - INIT_DELAYED_WORK(&dev->sbutton_query_work, em28xx_query_sbutton); - - input_dev->name = "em28xx snapshot button"; - input_dev->phys = dev->snapshot_button_path; - input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP); - set_bit(EM28XX_SNAPSHOT_KEY, input_dev->keybit); - input_dev->keycodesize = 0; - input_dev->keycodemax = 0; - input_dev->id.bustype = BUS_USB; - input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor); - input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct); - input_dev->id.version = 1; - input_dev->dev.parent = &dev->udev->dev; - - err = input_register_device(input_dev); - if (err) { - em28xx_errdev("input_register_device failed\n"); - input_free_device(input_dev); - return; - } - - dev->sbutton_input_dev = input_dev; - schedule_delayed_work(&dev->sbutton_query_work, - msecs_to_jiffies(EM28XX_SBUTTON_QUERY_INTERVAL)); - return; - + em28xx_unregister_extension(&rc_ops); } -void em28xx_deregister_snapshot_button(struct em28xx *dev) -{ - if (dev->sbutton_input_dev != NULL) { - em28xx_info("Deregistering snapshot button\n"); - cancel_delayed_work_sync(&dev->sbutton_query_work); - input_unregister_device(dev->sbutton_input_dev); - dev->sbutton_input_dev = NULL; - } - return; -} +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Mauro Carvalho Chehab "); +MODULE_DESCRIPTION("Em28xx Input driver"); + +module_init(em28xx_rc_register); +module_exit(em28xx_rc_unregister); diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c index 324b695c0724..50f5f4fc2148 100644 --- a/drivers/media/video/em28xx/em28xx-video.c +++ b/drivers/media/video/em28xx/em28xx-video.c @@ -1305,9 +1305,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) if (0 == INPUT(i)->type) return -EINVAL; - dev->ctl_input = i; - - video_mux(dev, dev->ctl_input); + video_mux(dev, i); return 0; } @@ -2262,6 +2260,7 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_release_resources(dev); kfree(dev->alt_max_pkt_size); kfree(dev); + kfree(fh); return 0; } @@ -2286,7 +2285,6 @@ static int em28xx_v4l2_close(struct file *filp) videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; - wake_up_interruptible_nr(&dev->open, 1); return 0; } @@ -2497,6 +2495,10 @@ static struct video_device *em28xx_vdev_init(struct em28xx *dev, vfd->release = video_device_release; vfd->debug = video_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); @@ -2518,7 +2520,6 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->norm = em28xx_video_template.current_norm; v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); dev->interlaced = EM28XX_INTERLACED_DEFAULT; - dev->ctl_input = 0; /* Analog specific initialization */ dev->format = &format[0]; @@ -2532,7 +2533,7 @@ int em28xx_register_analog_devices(struct em28xx *dev) em28xx_set_video_format(dev, format[0].fourcc, maxw, norm_maxh(dev)); - video_mux(dev, dev->ctl_input); + video_mux(dev, 0); /* Audio defaults */ dev->mute = 1; diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 2868b19f8b54..8757523e6863 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -226,24 +226,10 @@ struct em28xx_usb_isoc_ctl { /* isoc transfer buffers for digital mode */ struct em28xx_usb_isoc_bufs digital_bufs; - /* Last buffer command and region */ - u8 cmd; - int pos, size, pktsize; - - /* Last field: ODD or EVEN? */ - int field; - - /* Stores incomplete commands */ - u32 tmp_buf; - int tmp_buf_len; - /* Stores already requested buffers */ struct em28xx_buffer *vid_buf; struct em28xx_buffer *vbi_buf; - /* Stores the number of received fields */ - int nfields; - /* isoc urb callback */ int (*isoc_copy) (struct em28xx *dev, struct urb *urb); @@ -264,12 +250,10 @@ struct em28xx_buffer { struct list_head frame; int top_field; - int receiving; }; struct em28xx_dmaqueue { struct list_head active; - struct list_head queued; wait_queue_head_t wq; @@ -277,13 +261,6 @@ struct em28xx_dmaqueue { int pos; }; -/* io methods */ -enum em28xx_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - /* inputs */ #define MAX_EM28XX_INPUT 4 @@ -467,6 +444,7 @@ enum em28xx_dev_state { /* em28xx extensions */ #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 +#define EM28XX_RC 0x30 /* em28xx resource types (used for res_get/res_lock etc */ #define EM28XX_RESOURCE_VIDEO 0x01 @@ -577,7 +555,6 @@ struct em28xx { /* states */ enum em28xx_dev_state state; - enum em28xx_io_method io; /* vbi related state tracking */ int capture_type; @@ -593,7 +570,6 @@ struct em28xx { struct mutex ctrl_urb_lock; /* protects urb_buf */ /* spinlock_t queue_lock; */ struct list_head inqueue, outqueue; - wait_queue_head_t open, wait_frame, wait_stream; struct video_device *vbi_dev; struct video_device *radio_dev; @@ -695,6 +671,7 @@ int em28xx_init_isoc(struct em28xx *dev, enum em28xx_mode mode, int max_packets, int num_bufs, int max_pkt_size, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)); void em28xx_uninit_isoc(struct em28xx *dev, enum em28xx_mode mode); +void em28xx_stop_urbs(struct em28xx *dev); int em28xx_isoc_dvb_max_packetsize(struct em28xx *dev); int em28xx_set_mode(struct em28xx *dev, enum em28xx_mode set_mode); int em28xx_gpio_set(struct em28xx *dev, struct em28xx_reg_seq *gpio); @@ -710,45 +687,12 @@ void em28xx_release_analog_resources(struct em28xx *dev); /* Provided by em28xx-cards.c */ extern int em2800_variant_detect(struct usb_device *udev, int model); -extern void em28xx_pre_card_setup(struct em28xx *dev); -extern void em28xx_card_setup(struct em28xx *dev); extern struct em28xx_board em28xx_boards[]; extern struct usb_device_id em28xx_id_table[]; extern const unsigned int em28xx_bcount; -void em28xx_register_i2c_ir(struct em28xx *dev); int em28xx_tuner_callback(void *ptr, int component, int command, int arg); void em28xx_release_resources(struct em28xx *dev); -/* Provided by em28xx-input.c */ - -#ifdef CONFIG_VIDEO_EM28XX_RC - -int em28xx_get_key_terratec(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -int em28xx_get_key_em_haup(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw); -int em28xx_get_key_pinnacle_usb_grey(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw); -int em28xx_get_key_winfast_usbii_deluxe(struct IR_i2c *ir, u32 *ir_key, - u32 *ir_raw); -void em28xx_register_snapshot_button(struct em28xx *dev); -void em28xx_deregister_snapshot_button(struct em28xx *dev); - -int em28xx_ir_init(struct em28xx *dev); -int em28xx_ir_fini(struct em28xx *dev); - -#else - -#define em28xx_get_key_terratec NULL -#define em28xx_get_key_em_haup NULL -#define em28xx_get_key_pinnacle_usb_grey NULL -#define em28xx_get_key_winfast_usbii_deluxe NULL - -static inline void em28xx_register_snapshot_button(struct em28xx *dev) {} -static inline void em28xx_deregister_snapshot_button(struct em28xx *dev) {} -static inline int em28xx_ir_init(struct em28xx *dev) { return 0; } -static inline int em28xx_ir_fini(struct em28xx *dev) { return 0; } - -#endif - /* Provided by em28xx-vbi.c */ extern struct videobuf_queue_ops em28xx_vbi_qops; diff --git a/drivers/media/video/et61x251/Kconfig b/drivers/media/video/et61x251/Kconfig deleted file mode 100644 index 87981b078fe6..000000000000 --- a/drivers/media/video/et61x251/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -config USB_ET61X251 - tristate "USB ET61X[12]51 PC Camera Controller support (DEPRECATED)" - depends on VIDEO_V4L2 - default n - ---help--- - This driver is DEPRECATED please use the gspca zc3xx module - instead. - - Say Y here if you want support for cameras based on Etoms ET61X151 - or ET61X251 PC Camera Controllers. - - See for more info. - - This driver uses the Video For Linux API. You must say Y or M to - "Video For Linux" to use this driver. - - To compile this driver as a module, choose M here: the - module will be called et61x251. diff --git a/drivers/media/video/et61x251/Makefile b/drivers/media/video/et61x251/Makefile deleted file mode 100644 index 2ff4db9ec882..000000000000 --- a/drivers/media/video/et61x251/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -et61x251-objs := et61x251_core.o et61x251_tas5130d1b.o - -obj-$(CONFIG_USB_ET61X251) += et61x251.o - diff --git a/drivers/media/video/et61x251/et61x251.h b/drivers/media/video/et61x251/et61x251.h deleted file mode 100644 index 337ded4a6388..000000000000 --- a/drivers/media/video/et61x251/et61x251.h +++ /dev/null @@ -1,213 +0,0 @@ -/*************************************************************************** - * V4L2 driver for ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006 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 * - * (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 _ET61X251_H_ -#define _ET61X251_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "et61x251_sensor.h" - -/*****************************************************************************/ - -#define ET61X251_DEBUG -#define ET61X251_DEBUG_LEVEL 2 -#define ET61X251_MAX_DEVICES 64 -#define ET61X251_PRESERVE_IMGSCALE 0 -#define ET61X251_FORCE_MUNMAP 0 -#define ET61X251_MAX_FRAMES 32 -#define ET61X251_COMPRESSION_QUALITY 0 -#define ET61X251_URBS 2 -#define ET61X251_ISO_PACKETS 7 -#define ET61X251_ALTERNATE_SETTING 13 -#define ET61X251_URB_TIMEOUT msecs_to_jiffies(2 * ET61X251_ISO_PACKETS) -#define ET61X251_CTRL_TIMEOUT 100 -#define ET61X251_FRAME_TIMEOUT 2 - -/*****************************************************************************/ - -static const struct usb_device_id et61x251_id_table[] = { - { USB_DEVICE(0x102c, 0x6251), }, - { } -}; - -ET61X251_SENSOR_TABLE - -/*****************************************************************************/ - -enum et61x251_frame_state { - F_UNUSED, - F_QUEUED, - F_GRABBING, - F_DONE, - F_ERROR, -}; - -struct et61x251_frame_t { - void* bufmem; - struct v4l2_buffer buf; - enum et61x251_frame_state state; - struct list_head frame; - unsigned long vma_use_count; -}; - -enum et61x251_dev_state { - DEV_INITIALIZED = 0x01, - DEV_DISCONNECTED = 0x02, - DEV_MISCONFIGURED = 0x04, -}; - -enum et61x251_io_method { - IO_NONE, - IO_READ, - IO_MMAP, -}; - -enum et61x251_stream_state { - STREAM_OFF, - STREAM_INTERRUPT, - STREAM_ON, -}; - -struct et61x251_sysfs_attr { - u8 reg, i2c_reg; -}; - -struct et61x251_module_param { - u8 force_munmap; - u16 frame_timeout; -}; - -static DEFINE_MUTEX(et61x251_sysfs_lock); -static DECLARE_RWSEM(et61x251_dev_lock); - -struct et61x251_device { - struct video_device* v4ldev; - - struct et61x251_sensor sensor; - - struct usb_device* usbdev; - struct urb* urb[ET61X251_URBS]; - void* transfer_buffer[ET61X251_URBS]; - u8* control_buffer; - - struct et61x251_frame_t *frame_current, frame[ET61X251_MAX_FRAMES]; - struct list_head inqueue, outqueue; - u32 frame_count, nbuffers, nreadbuffers; - - enum et61x251_io_method io; - enum et61x251_stream_state stream; - - struct v4l2_jpegcompression compression; - - struct et61x251_sysfs_attr sysfs; - struct et61x251_module_param module_param; - - struct kref kref; - enum et61x251_dev_state state; - u8 users; - - struct completion probe; - struct mutex open_mutex, fileop_mutex; - spinlock_t queue_lock; - wait_queue_head_t wait_open, wait_frame, wait_stream; -}; - -/*****************************************************************************/ - -struct et61x251_device* -et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id) -{ - return usb_match_id(usb_ifnum_to_if(cam->usbdev, 0), id) ? cam : NULL; -} - - -void -et61x251_attach_sensor(struct et61x251_device* cam, - const struct et61x251_sensor* sensor) -{ - memcpy(&cam->sensor, sensor, sizeof(struct et61x251_sensor)); -} - -/*****************************************************************************/ - -#undef DBG -#undef KDBG -#ifdef ET61X251_DEBUG -#define DBG(level, fmt, ...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1) \ - dev_err(&cam->usbdev->dev, fmt "\n", \ - ##__VA_ARGS__); \ - else if ((level) == 2) \ - dev_info(&cam->usbdev->dev, fmt "\n", \ - ##__VA_ARGS__); \ - else if ((level) >= 3) \ - dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, \ - ##__VA_ARGS__); \ - } \ -} while (0) -#define KDBG(level, fmt, ...) \ -do { \ - if (debug >= (level)) { \ - if ((level) == 1 || (level) == 2) \ - pr_info(fmt "\n", ##__VA_ARGS__); \ - else if ((level) == 3) \ - pr_debug("[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, \ - ##__VA_ARGS__); \ - } \ -} while (0) -#define V4LDBG(level, name, cmd) \ -do { \ - if (debug >= (level)) \ - v4l_print_ioctl(name, cmd); \ -} while (0) -#else -#define DBG(level, fmt, ...) do {;} while(0) -#define KDBG(level, fmt, ...) do {;} while(0) -#define V4LDBG(level, name, cmd) do {;} while(0) -#endif - -#undef PDBG -#define PDBG(fmt, ...) \ - dev_info(&cam->usbdev->dev, "[%s:%s:%d] " fmt "\n", \ - __FILE__, __func__, __LINE__, ##__VA_ARGS__) - -#undef PDBGG -#define PDBGG(fmt, args...) do {;} while (0) /* placeholder */ - -#endif /* _ET61X251_H_ */ diff --git a/drivers/media/video/et61x251/et61x251_core.c b/drivers/media/video/et61x251/et61x251_core.c deleted file mode 100644 index 5539f09440ac..000000000000 --- a/drivers/media/video/et61x251/et61x251_core.c +++ /dev/null @@ -1,2683 +0,0 @@ -/*************************************************************************** - * V4L2 driver for ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006-2007 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 * - * (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. * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "et61x251.h" - -/*****************************************************************************/ - -#define ET61X251_MODULE_NAME "V4L2 driver for ET61X[12]51 " \ - "PC Camera Controllers" -#define ET61X251_MODULE_AUTHOR "(C) 2006-2007 Luca Risolia" -#define ET61X251_AUTHOR_EMAIL "" -#define ET61X251_MODULE_LICENSE "GPL" -#define ET61X251_MODULE_VERSION "1.1.10" - -/*****************************************************************************/ - -MODULE_DEVICE_TABLE(usb, et61x251_id_table); - -MODULE_AUTHOR(ET61X251_MODULE_AUTHOR " " ET61X251_AUTHOR_EMAIL); -MODULE_DESCRIPTION(ET61X251_MODULE_NAME); -MODULE_VERSION(ET61X251_MODULE_VERSION); -MODULE_LICENSE(ET61X251_MODULE_LICENSE); - -static short video_nr[] = {[0 ... ET61X251_MAX_DEVICES-1] = -1}; -module_param_array(video_nr, short, NULL, 0444); -MODULE_PARM_DESC(video_nr, - "\n<-1|n[,...]> Specify V4L2 minor mode number." - "\n -1 = use next available (default)" - "\n n = use minor number n (integer >= 0)" - "\nYou can specify up to " - __MODULE_STRING(ET61X251_MAX_DEVICES) " cameras this way." - "\nFor example:" - "\nvideo_nr=-1,2,-1 would assign minor number 2 to" - "\nthe second registered camera and use auto for the first" - "\none and for every other camera." - "\n"); - -static bool force_munmap[] = {[0 ... ET61X251_MAX_DEVICES-1] = - ET61X251_FORCE_MUNMAP}; -module_param_array(force_munmap, bool, NULL, 0444); -MODULE_PARM_DESC(force_munmap, - "\n<0|1[,...]> Force the application to unmap previously" - "\nmapped buffer memory before calling any VIDIOC_S_CROP or" - "\nVIDIOC_S_FMT ioctl's. Not all the applications support" - "\nthis feature. This parameter is specific for each" - "\ndetected camera." - "\n 0 = do not force memory unmapping" - "\n 1 = force memory unmapping (save memory)" - "\nDefault value is "__MODULE_STRING(ET61X251_FORCE_MUNMAP)"." - "\n"); - -static unsigned int frame_timeout[] = {[0 ... ET61X251_MAX_DEVICES-1] = - ET61X251_FRAME_TIMEOUT}; -module_param_array(frame_timeout, uint, NULL, 0644); -MODULE_PARM_DESC(frame_timeout, - "\n Timeout for a video frame in seconds." - "\nThis parameter is specific for each detected camera." - "\nDefault value is " - __MODULE_STRING(ET61X251_FRAME_TIMEOUT)"." - "\n"); - -#ifdef ET61X251_DEBUG -static unsigned short debug = ET61X251_DEBUG_LEVEL; -module_param(debug, ushort, 0644); -MODULE_PARM_DESC(debug, - "\n Debugging information level, from 0 to 3:" - "\n0 = none (use carefully)" - "\n1 = critical errors" - "\n2 = significant informations" - "\n3 = more verbose messages" - "\nLevel 3 is useful for testing only, when only " - "one device is used." - "\nDefault value is "__MODULE_STRING(ET61X251_DEBUG_LEVEL)"." - "\n"); -#endif - -/*****************************************************************************/ - -static u32 -et61x251_request_buffers(struct et61x251_device* cam, u32 count, - enum et61x251_io_method io) -{ - struct v4l2_pix_format* p = &(cam->sensor.pix_format); - struct v4l2_rect* r = &(cam->sensor.cropcap.bounds); - const size_t imagesize = cam->module_param.force_munmap || - io == IO_READ ? - (p->width * p->height * p->priv) / 8 : - (r->width * r->height * p->priv) / 8; - void* buff = NULL; - u32 i; - - if (count > ET61X251_MAX_FRAMES) - count = ET61X251_MAX_FRAMES; - - cam->nbuffers = count; - while (cam->nbuffers > 0) { - if ((buff = vmalloc_32_user(cam->nbuffers * - PAGE_ALIGN(imagesize)))) - break; - cam->nbuffers--; - } - - for (i = 0; i < cam->nbuffers; i++) { - cam->frame[i].bufmem = buff + i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.index = i; - cam->frame[i].buf.m.offset = i*PAGE_ALIGN(imagesize); - cam->frame[i].buf.length = imagesize; - cam->frame[i].buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cam->frame[i].buf.sequence = 0; - cam->frame[i].buf.field = V4L2_FIELD_NONE; - cam->frame[i].buf.memory = V4L2_MEMORY_MMAP; - cam->frame[i].buf.flags = 0; - } - - return cam->nbuffers; -} - - -static void et61x251_release_buffers(struct et61x251_device* cam) -{ - if (cam->nbuffers) { - vfree(cam->frame[0].bufmem); - cam->nbuffers = 0; - } - cam->frame_current = NULL; -} - - -static void et61x251_empty_framequeues(struct et61x251_device* cam) -{ - u32 i; - - INIT_LIST_HEAD(&cam->inqueue); - INIT_LIST_HEAD(&cam->outqueue); - - for (i = 0; i < ET61X251_MAX_FRAMES; i++) { - cam->frame[i].state = F_UNUSED; - cam->frame[i].buf.bytesused = 0; - } -} - - -static void et61x251_requeue_outqueue(struct et61x251_device* cam) -{ - struct et61x251_frame_t *i; - - list_for_each_entry(i, &cam->outqueue, frame) { - i->state = F_QUEUED; - list_add(&i->frame, &cam->inqueue); - } - - INIT_LIST_HEAD(&cam->outqueue); -} - - -static void et61x251_queue_unusedframes(struct et61x251_device* cam) -{ - unsigned long lock_flags; - u32 i; - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].state == F_UNUSED) { - cam->frame[i].state = F_QUEUED; - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[i].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - } -} - -/*****************************************************************************/ - -int et61x251_write_reg(struct et61x251_device* cam, u8 value, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - *buff = value; - - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) { - DBG(3, "Failed to write a register (value 0x%02X, index " - "0x%02X, error %d)", value, index, res); - return -1; - } - - return 0; -} - - -static int et61x251_read_reg(struct et61x251_device* cam, u16 index) -{ - struct usb_device* udev = cam->usbdev; - u8* buff = cam->control_buffer; - int res; - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0, index, buff, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - DBG(3, "Failed to read a register (index 0x%02X, error %d)", - index, res); - - return (res >= 0) ? (int)(*buff) : -1; -} - - -static int -et61x251_i2c_wait(struct et61x251_device* cam, - const struct et61x251_sensor* sensor) -{ - int i, r; - - for (i = 1; i <= 8; i++) { - if (sensor->interface == ET61X251_I2C_3WIRES) { - r = et61x251_read_reg(cam, 0x8e); - if (!(r & 0x02) && (r >= 0)) - return 0; - } else { - r = et61x251_read_reg(cam, 0x8b); - if (!(r & 0x01) && (r >= 0)) - return 0; - } - if (r < 0) - return -EIO; - udelay(8*8); /* minimum for sensors at 400kHz */ - } - - return -EBUSY; -} - - -int -et61x251_i2c_raw_write(struct et61x251_device* cam, u8 n, u8 data1, u8 data2, - u8 data3, u8 data4, u8 data5, u8 data6, u8 data7, - u8 data8, u8 address) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = data2; - data[1] = data3; - data[2] = data4; - data[3] = data5; - data[4] = data6; - data[5] = data7; - data[6] = data8; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x81, data, n-1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x02 | (n << 4); - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - /* Start writing through the serial interface */ - data[0] = data1; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, &cam->sensor); - - if (err) - DBG(3, "I2C raw write failed for %s image sensor", - cam->sensor.name); - - PDBGG("I2C raw write: %u bytes, address = 0x%02X, data1 = 0x%02X, " - "data2 = 0x%02X, data3 = 0x%02X, data4 = 0x%02X, data5 = 0x%02X," - " data6 = 0x%02X, data7 = 0x%02X, data8 = 0x%02X", n, address, - data1, data2, data3, data4, data5, data6, data7, data8); - - return err ? -1 : 0; - -} - - -/*****************************************************************************/ - -static void et61x251_urb_complete(struct urb *urb) -{ - struct et61x251_device* cam = urb->context; - struct et61x251_frame_t** f; - size_t imagesize; - u8 i; - int err = 0; - - if (urb->status == -ENOENT) - return; - - f = &cam->frame_current; - - if (cam->stream == STREAM_INTERRUPT) { - cam->stream = STREAM_OFF; - if ((*f)) - (*f)->state = F_QUEUED; - DBG(3, "Stream interrupted"); - wake_up(&cam->wait_stream); - } - - if (cam->state & DEV_DISCONNECTED) - return; - - if (cam->state & DEV_MISCONFIGURED) { - wake_up_interruptible(&cam->wait_frame); - return; - } - - if (cam->stream == STREAM_OFF || list_empty(&cam->inqueue)) - goto resubmit_urb; - - if (!(*f)) - (*f) = list_entry(cam->inqueue.next, struct et61x251_frame_t, - frame); - - imagesize = (cam->sensor.pix_format.width * - cam->sensor.pix_format.height * - cam->sensor.pix_format.priv) / 8; - - for (i = 0; i < urb->number_of_packets; i++) { - unsigned int len, status; - void *pos; - u8* b1, * b2, sof; - const u8 VOID_BYTES = 6; - size_t imglen; - - len = urb->iso_frame_desc[i].actual_length; - status = urb->iso_frame_desc[i].status; - pos = urb->iso_frame_desc[i].offset + urb->transfer_buffer; - - if (status) { - DBG(3, "Error in isochronous frame"); - (*f)->state = F_ERROR; - continue; - } - - b1 = pos++; - b2 = pos++; - sof = ((*b1 & 0x3f) == 63); - imglen = ((*b1 & 0xc0) << 2) | *b2; - - PDBGG("Isochrnous frame: length %u, #%u i, image length %zu", - len, i, imglen); - - if ((*f)->state == F_QUEUED || (*f)->state == F_ERROR) -start_of_frame: - if (sof) { - (*f)->state = F_GRABBING; - (*f)->buf.bytesused = 0; - do_gettimeofday(&(*f)->buf.timestamp); - pos += 22; - DBG(3, "SOF detected: new video frame"); - } - - if ((*f)->state == F_GRABBING) { - if (sof && (*f)->buf.bytesused) { - if (cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_ET61X251) - goto end_of_frame; - else { - DBG(3, "Not expected SOF detected " - "after %lu bytes", - (unsigned long)(*f)->buf.bytesused); - (*f)->state = F_ERROR; - continue; - } - } - - if ((*f)->buf.bytesused + imglen > imagesize) { - DBG(3, "Video frame size exceeded"); - (*f)->state = F_ERROR; - continue; - } - - pos += VOID_BYTES; - - memcpy((*f)->bufmem+(*f)->buf.bytesused, pos, imglen); - (*f)->buf.bytesused += imglen; - - if ((*f)->buf.bytesused == imagesize) { - u32 b; -end_of_frame: - b = (*f)->buf.bytesused; - (*f)->state = F_DONE; - (*f)->buf.sequence= ++cam->frame_count; - spin_lock(&cam->queue_lock); - list_move_tail(&(*f)->frame, &cam->outqueue); - if (!list_empty(&cam->inqueue)) - (*f) = list_entry(cam->inqueue.next, - struct et61x251_frame_t, - frame); - else - (*f) = NULL; - spin_unlock(&cam->queue_lock); - DBG(3, "Video frame captured: : %lu bytes", - (unsigned long)(b)); - - if (!(*f)) - goto resubmit_urb; - - if (sof && - cam->sensor.pix_format.pixelformat == - V4L2_PIX_FMT_ET61X251) - goto start_of_frame; - } - } - } - -resubmit_urb: - urb->dev = cam->usbdev; - err = usb_submit_urb(urb, GFP_ATOMIC); - if (err < 0 && err != -EPERM) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "usb_submit_urb() failed"); - } - - wake_up_interruptible(&cam->wait_frame); -} - - -static int et61x251_start_transfer(struct et61x251_device* cam) -{ - struct usb_device *udev = cam->usbdev; - struct urb* urb; - struct usb_host_interface* altsetting = usb_altnum_to_altsetting( - usb_ifnum_to_if(udev, 0), - ET61X251_ALTERNATE_SETTING); - const unsigned int psz = le16_to_cpu(altsetting-> - endpoint[0].desc.wMaxPacketSize); - s8 i, j; - int err = 0; - - for (i = 0; i < ET61X251_URBS; i++) { - cam->transfer_buffer[i] = kzalloc(ET61X251_ISO_PACKETS * psz, - GFP_KERNEL); - if (!cam->transfer_buffer[i]) { - err = -ENOMEM; - DBG(1, "Not enough memory"); - goto free_buffers; - } - } - - for (i = 0; i < ET61X251_URBS; i++) { - urb = usb_alloc_urb(ET61X251_ISO_PACKETS, GFP_KERNEL); - cam->urb[i] = urb; - if (!urb) { - err = -ENOMEM; - DBG(1, "usb_alloc_urb() failed"); - goto free_urbs; - } - urb->dev = udev; - urb->context = cam; - urb->pipe = usb_rcvisocpipe(udev, 1); - urb->transfer_flags = URB_ISO_ASAP; - urb->number_of_packets = ET61X251_ISO_PACKETS; - urb->complete = et61x251_urb_complete; - urb->transfer_buffer = cam->transfer_buffer[i]; - urb->transfer_buffer_length = psz * ET61X251_ISO_PACKETS; - urb->interval = 1; - for (j = 0; j < ET61X251_ISO_PACKETS; j++) { - urb->iso_frame_desc[j].offset = psz * j; - urb->iso_frame_desc[j].length = psz; - } - } - - err = et61x251_write_reg(cam, 0x01, 0x03); - err = et61x251_write_reg(cam, 0x00, 0x03); - err = et61x251_write_reg(cam, 0x08, 0x03); - if (err) { - err = -EIO; - DBG(1, "I/O hardware error"); - goto free_urbs; - } - - err = usb_set_interface(udev, 0, ET61X251_ALTERNATE_SETTING); - if (err) { - DBG(1, "usb_set_interface() failed"); - goto free_urbs; - } - - cam->frame_current = NULL; - - for (i = 0; i < ET61X251_URBS; i++) { - err = usb_submit_urb(cam->urb[i], GFP_KERNEL); - if (err) { - for (j = i-1; j >= 0; j--) - usb_kill_urb(cam->urb[j]); - DBG(1, "usb_submit_urb() failed, error %d", err); - goto free_urbs; - } - } - - return 0; - -free_urbs: - for (i = 0; (i < ET61X251_URBS) && cam->urb[i]; i++) - usb_free_urb(cam->urb[i]); - -free_buffers: - for (i = 0; (i < ET61X251_URBS) && cam->transfer_buffer[i]; i++) - kfree(cam->transfer_buffer[i]); - - return err; -} - - -static int et61x251_stop_transfer(struct et61x251_device* cam) -{ - struct usb_device *udev = cam->usbdev; - s8 i; - int err = 0; - - if (cam->state & DEV_DISCONNECTED) - return 0; - - for (i = ET61X251_URBS-1; i >= 0; i--) { - usb_kill_urb(cam->urb[i]); - usb_free_urb(cam->urb[i]); - kfree(cam->transfer_buffer[i]); - } - - err = usb_set_interface(udev, 0, 0); /* 0 Mb/s */ - if (err) - DBG(3, "usb_set_interface() failed"); - - return err; -} - - -static int et61x251_stream_interrupt(struct et61x251_device* cam) -{ - long timeout; - - cam->stream = STREAM_INTERRUPT; - timeout = wait_event_timeout(cam->wait_stream, - (cam->stream == STREAM_OFF) || - (cam->state & DEV_DISCONNECTED), - ET61X251_URB_TIMEOUT); - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - else if (cam->stream != STREAM_OFF) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "URB timeout reached. The camera is misconfigured. To " - "use it, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - return 0; -} - -/*****************************************************************************/ - -#ifdef CONFIG_VIDEO_ADV_DEBUG - -static int et61x251_i2c_try_read(struct et61x251_device* cam, - const struct et61x251_sensor* sensor, - u8 address) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x10; - data[3] = !(et61x251_read_reg(cam, 0x8b) & 0x02); - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 4, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, sensor); - - res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, 0xc1, - 0, 0x80, data, 8, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - if (err) - DBG(3, "I2C read failed for %s image sensor", sensor->name); - - PDBGG("I2C read: address 0x%02X, value: 0x%02X", address, data[0]); - - return err ? -1 : (int)data[0]; -} - - -static int et61x251_i2c_try_write(struct et61x251_device* cam, - const struct et61x251_sensor* sensor, - u8 address, u8 value) -{ - struct usb_device* udev = cam->usbdev; - u8* data = cam->control_buffer; - int err = 0, res; - - data[0] = address; - data[1] = cam->sensor.i2c_slave_id; - data[2] = cam->sensor.rsta | 0x12; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x88, data, 3, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - data[0] = value; - res = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, 0x41, - 0, 0x80, data, 1, ET61X251_CTRL_TIMEOUT); - if (res < 0) - err += res; - - err += et61x251_i2c_wait(cam, sensor); - - if (err) - DBG(3, "I2C write failed for %s image sensor", sensor->name); - - PDBGG("I2C write: address 0x%02X, value: 0x%02X", address, value); - - return err ? -1 : 0; -} - -static int et61x251_i2c_read(struct et61x251_device* cam, u8 address) -{ - return et61x251_i2c_try_read(cam, &cam->sensor, address); -} - -static int et61x251_i2c_write(struct et61x251_device* cam, - u8 address, u8 value) -{ - return et61x251_i2c_try_write(cam, &cam->sensor, address, value); -} - -static u8 et61x251_strtou8(const char* buff, size_t len, ssize_t* count) -{ - char str[5]; - char* endp; - unsigned long val; - - if (len < 4) { - strncpy(str, buff, len); - str[len] = '\0'; - } else { - strncpy(str, buff, 4); - str[4] = '\0'; - } - - val = simple_strtoul(str, &endp, 0); - - *count = 0; - if (val <= 0xff) - *count = (ssize_t)(endp - str); - if ((*count) && (len == *count+1) && (buff[*count] == '\n')) - *count += 1; - - return (u8)val; -} - -/* - NOTE 1: being inside one of the following methods implies that the v4l - device exists for sure (see kobjects and reference counters) - NOTE 2: buffers are PAGE_SIZE long -*/ - -static ssize_t et61x251_show_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.reg); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_reg(struct device* cd, - struct device_attribute *attr, const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 index; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - index = et61x251_strtou8(buf, len, &count); - if (index > 0x8e || !count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.reg = index; - - DBG(2, "Moved ET61X[12]51 register index to 0x%02X", cam->sysfs.reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if ((val = et61x251_read_reg(cam, cam->sysfs.reg)) < 0) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - value = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - err = et61x251_write_reg(cam, value, cam->sysfs.reg); - if (err) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - DBG(2, "Written ET61X[12]51 reg. 0x%02X, val. 0x%02X", - cam->sysfs.reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_i2c_reg(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - count = sprintf(buf, "%u\n", cam->sysfs.i2c_reg); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_i2c_reg(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 index; - ssize_t count; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - index = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - cam->sysfs.i2c_reg = index; - - DBG(2, "Moved sensor register index to 0x%02X", cam->sysfs.i2c_reg); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t et61x251_show_i2c_val(struct device* cd, - struct device_attribute *attr, char* buf) -{ - struct et61x251_device* cam; - ssize_t count; - int val; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENOSYS; - } - - if ((val = et61x251_i2c_read(cam, cam->sysfs.i2c_reg)) < 0) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - count = sprintf(buf, "%d\n", val); - - DBG(3, "Read bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static ssize_t -et61x251_store_i2c_val(struct device* cd, struct device_attribute *attr, - const char* buf, size_t len) -{ - struct et61x251_device* cam; - u8 value; - ssize_t count; - int err; - - if (mutex_lock_interruptible(&et61x251_sysfs_lock)) - return -ERESTARTSYS; - - cam = video_get_drvdata(to_video_device(cd)); - if (!cam) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENODEV; - } - - if (!(cam->sensor.sysfs_ops & ET61X251_I2C_READ)) { - mutex_unlock(&et61x251_sysfs_lock); - return -ENOSYS; - } - - value = et61x251_strtou8(buf, len, &count); - if (!count) { - mutex_unlock(&et61x251_sysfs_lock); - return -EINVAL; - } - - err = et61x251_i2c_write(cam, cam->sysfs.i2c_reg, value); - if (err) { - mutex_unlock(&et61x251_sysfs_lock); - return -EIO; - } - - DBG(2, "Written sensor reg. 0x%02X, val. 0x%02X", - cam->sysfs.i2c_reg, value); - DBG(3, "Written bytes: %zd", count); - - mutex_unlock(&et61x251_sysfs_lock); - - return count; -} - - -static DEVICE_ATTR(reg, S_IRUGO | S_IWUSR, - et61x251_show_reg, et61x251_store_reg); -static DEVICE_ATTR(val, S_IRUGO | S_IWUSR, - et61x251_show_val, et61x251_store_val); -static DEVICE_ATTR(i2c_reg, S_IRUGO | S_IWUSR, - et61x251_show_i2c_reg, et61x251_store_i2c_reg); -static DEVICE_ATTR(i2c_val, S_IRUGO | S_IWUSR, - et61x251_show_i2c_val, et61x251_store_i2c_val); - - -static int et61x251_create_sysfs(struct et61x251_device* cam) -{ - struct device *classdev = &(cam->v4ldev->dev); - int err = 0; - - if ((err = device_create_file(classdev, &dev_attr_reg))) - goto err_out; - if ((err = device_create_file(classdev, &dev_attr_val))) - goto err_reg; - - if (cam->sensor.sysfs_ops) { - if ((err = device_create_file(classdev, &dev_attr_i2c_reg))) - goto err_val; - if ((err = device_create_file(classdev, &dev_attr_i2c_val))) - goto err_i2c_reg; - } - -err_i2c_reg: - if (cam->sensor.sysfs_ops) - device_remove_file(classdev, &dev_attr_i2c_reg); -err_val: - device_remove_file(classdev, &dev_attr_val); -err_reg: - device_remove_file(classdev, &dev_attr_reg); -err_out: - return err; -} -#endif /* CONFIG_VIDEO_ADV_DEBUG */ - -/*****************************************************************************/ - -static int -et61x251_set_pix_format(struct et61x251_device* cam, - struct v4l2_pix_format* pix) -{ - int r, err = 0; - - if ((r = et61x251_read_reg(cam, 0x12)) < 0) - err += r; - if (pix->pixelformat == V4L2_PIX_FMT_ET61X251) - err += et61x251_write_reg(cam, r & 0xfd, 0x12); - else - err += et61x251_write_reg(cam, r | 0x02, 0x12); - - return err ? -EIO : 0; -} - - -static int -et61x251_set_compression(struct et61x251_device* cam, - struct v4l2_jpegcompression* compression) -{ - int r, err = 0; - - if ((r = et61x251_read_reg(cam, 0x12)) < 0) - err += r; - if (compression->quality == 0) - err += et61x251_write_reg(cam, r & 0xfb, 0x12); - else - err += et61x251_write_reg(cam, r | 0x04, 0x12); - - return err ? -EIO : 0; -} - - -static int et61x251_set_scale(struct et61x251_device* cam, u8 scale) -{ - int r = 0, err = 0; - - r = et61x251_read_reg(cam, 0x12); - if (r < 0) - err += r; - - if (scale == 1) - err += et61x251_write_reg(cam, r & ~0x01, 0x12); - else if (scale == 2) - err += et61x251_write_reg(cam, r | 0x01, 0x12); - - if (err) - return -EIO; - - PDBGG("Scaling factor: %u", scale); - - return 0; -} - - -static int -et61x251_set_crop(struct et61x251_device* cam, struct v4l2_rect* rect) -{ - struct et61x251_sensor* s = &cam->sensor; - u16 fmw_sx = (u16)(rect->left - s->cropcap.bounds.left + - s->active_pixel.left), - fmw_sy = (u16)(rect->top - s->cropcap.bounds.top + - s->active_pixel.top), - fmw_length = (u16)(rect->width), - fmw_height = (u16)(rect->height); - int err = 0; - - err += et61x251_write_reg(cam, fmw_sx & 0xff, 0x69); - err += et61x251_write_reg(cam, fmw_sy & 0xff, 0x6a); - err += et61x251_write_reg(cam, fmw_length & 0xff, 0x6b); - err += et61x251_write_reg(cam, fmw_height & 0xff, 0x6c); - err += et61x251_write_reg(cam, (fmw_sx >> 8) | ((fmw_sy & 0x300) >> 6) - | ((fmw_length & 0x300) >> 4) - | ((fmw_height & 0x300) >> 2), 0x6d); - if (err) - return -EIO; - - PDBGG("fmw_sx, fmw_sy, fmw_length, fmw_height: %u %u %u %u", - fmw_sx, fmw_sy, fmw_length, fmw_height); - - return 0; -} - - -static int et61x251_init(struct et61x251_device* cam) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - struct v4l2_queryctrl *qctrl; - struct v4l2_rect* rect; - u8 i = 0; - int err = 0; - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->open_mutex); - init_waitqueue_head(&cam->wait_open); - qctrl = s->qctrl; - rect = &(s->cropcap.defrect); - cam->compression.quality = ET61X251_COMPRESSION_QUALITY; - } else { /* use current values */ - qctrl = s->_qctrl; - rect = &(s->_rect); - } - - err += et61x251_set_scale(cam, rect->width / s->pix_format.width); - err += et61x251_set_crop(cam, rect); - if (err) - return err; - - if (s->init) { - err = s->init(cam); - if (err) { - DBG(3, "Sensor initialization failed"); - return err; - } - } - - err += et61x251_set_compression(cam, &cam->compression); - err += et61x251_set_pix_format(cam, &s->pix_format); - if (s->set_pix_format) - err += s->set_pix_format(cam, &s->pix_format); - if (err) - return err; - - if (s->pix_format.pixelformat == V4L2_PIX_FMT_ET61X251) - DBG(3, "Compressed video format is active, quality %d", - cam->compression.quality); - else - DBG(3, "Uncompressed video format is active"); - - if (s->set_crop) - if ((err = s->set_crop(cam, rect))) { - DBG(3, "set_crop() failed"); - return err; - } - - if (s->set_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (s->qctrl[i].id != 0 && - !(s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED)) { - ctrl.id = s->qctrl[i].id; - ctrl.value = qctrl[i].default_value; - err = s->set_ctrl(cam, &ctrl); - if (err) { - DBG(3, "Set %s control failed", - s->qctrl[i].name); - return err; - } - DBG(3, "Image sensor supports '%s' control", - s->qctrl[i].name); - } - } - - if (!(cam->state & DEV_INITIALIZED)) { - mutex_init(&cam->fileop_mutex); - spin_lock_init(&cam->queue_lock); - init_waitqueue_head(&cam->wait_frame); - init_waitqueue_head(&cam->wait_stream); - cam->nreadbuffers = 2; - memcpy(s->_qctrl, s->qctrl, sizeof(s->qctrl)); - memcpy(&(s->_rect), &(s->cropcap.defrect), - sizeof(struct v4l2_rect)); - cam->state |= DEV_INITIALIZED; - } - - DBG(2, "Initialization succeeded"); - return 0; -} - -/*****************************************************************************/ - -static void et61x251_release_resources(struct kref *kref) -{ - struct et61x251_device *cam; - - mutex_lock(&et61x251_sysfs_lock); - - cam = container_of(kref, struct et61x251_device, kref); - - DBG(2, "V4L2 device %s deregistered", - video_device_node_name(cam->v4ldev)); - video_set_drvdata(cam->v4ldev, NULL); - video_unregister_device(cam->v4ldev); - usb_put_dev(cam->usbdev); - kfree(cam->control_buffer); - kfree(cam); - - mutex_unlock(&et61x251_sysfs_lock); -} - - -static int et61x251_open(struct file *filp) -{ - struct et61x251_device* cam; - int err = 0; - - if (!down_read_trylock(&et61x251_dev_lock)) - return -ERESTARTSYS; - - cam = video_drvdata(filp); - - if (wait_for_completion_interruptible(&cam->probe)) { - up_read(&et61x251_dev_lock); - return -ERESTARTSYS; - } - - kref_get(&cam->kref); - - if (mutex_lock_interruptible(&cam->open_mutex)) { - kref_put(&cam->kref, et61x251_release_resources); - up_read(&et61x251_dev_lock); - return -ERESTARTSYS; - } - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - err = -ENODEV; - goto out; - } - - if (cam->users) { - DBG(2, "Device %s is already in use", - video_device_node_name(cam->v4ldev)); - DBG(3, "Simultaneous opens are not supported"); - if ((filp->f_flags & O_NONBLOCK) || - (filp->f_flags & O_NDELAY)) { - err = -EWOULDBLOCK; - goto out; - } - DBG(2, "A blocking open() has been requested. Wait for the " - "device to be released..."); - up_read(&et61x251_dev_lock); - err = wait_event_interruptible_exclusive(cam->wait_open, - (cam->state & DEV_DISCONNECTED) - || !cam->users); - down_read(&et61x251_dev_lock); - if (err) - goto out; - if (cam->state & DEV_DISCONNECTED) { - err = -ENODEV; - goto out; - } - } - - if (cam->state & DEV_MISCONFIGURED) { - err = et61x251_init(cam); - if (err) { - DBG(1, "Initialization failed again. " - "I will retry on next open()."); - goto out; - } - cam->state &= ~DEV_MISCONFIGURED; - } - - if ((err = et61x251_start_transfer(cam))) - goto out; - - filp->private_data = cam; - cam->users++; - cam->io = IO_NONE; - cam->stream = STREAM_OFF; - cam->nbuffers = 0; - cam->frame_count = 0; - et61x251_empty_framequeues(cam); - - DBG(3, "Video device %s is open", - video_device_node_name(cam->v4ldev)); - -out: - mutex_unlock(&cam->open_mutex); - if (err) - kref_put(&cam->kref, et61x251_release_resources); - up_read(&et61x251_dev_lock); - return err; -} - - -static int et61x251_release(struct file *filp) -{ - struct et61x251_device* cam; - - down_write(&et61x251_dev_lock); - - cam = video_drvdata(filp); - - et61x251_stop_transfer(cam); - et61x251_release_buffers(cam); - cam->users--; - wake_up_interruptible_nr(&cam->wait_open, 1); - - DBG(3, "Video device %s closed", - video_device_node_name(cam->v4ldev)); - - kref_put(&cam->kref, et61x251_release_resources); - - up_write(&et61x251_dev_lock); - - return 0; -} - - -static ssize_t -et61x251_read(struct file* filp, char __user * buf, - size_t count, loff_t* f_pos) -{ - struct et61x251_device *cam = video_drvdata(filp); - struct et61x251_frame_t* f, * i; - unsigned long lock_flags; - long timeout; - int err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (cam->io == IO_MMAP) { - DBG(3, "Close and open the device again to choose the read " - "method"); - mutex_unlock(&cam->fileop_mutex); - return -EBUSY; - } - - if (cam->io == IO_NONE) { - if (!et61x251_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "read() failed, not enough memory"); - mutex_unlock(&cam->fileop_mutex); - return -ENOMEM; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (list_empty(&cam->inqueue)) { - if (!list_empty(&cam->outqueue)) - et61x251_empty_framequeues(cam); - et61x251_queue_unusedframes(cam); - } - - if (!count) { - mutex_unlock(&cam->fileop_mutex); - return 0; - } - - if (list_empty(&cam->outqueue)) { - if (filp->f_flags & O_NONBLOCK) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - msecs_to_jiffies( - cam->module_param.frame_timeout * 1000 - ) - ); - if (timeout < 0) { - mutex_unlock(&cam->fileop_mutex); - return timeout; - } - if (cam->state & DEV_DISCONNECTED) { - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - if (!timeout || (cam->state & DEV_MISCONFIGURED)) { - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - } - - f = list_entry(cam->outqueue.prev, struct et61x251_frame_t, frame); - - if (count > f->buf.bytesused) - count = f->buf.bytesused; - - if (copy_to_user(buf, f->bufmem, count)) { - err = -EFAULT; - goto exit; - } - *f_pos += count; - -exit: - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(i, &cam->outqueue, frame) - i->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - et61x251_queue_unusedframes(cam); - - PDBGG("Frame #%lu, bytes read: %zu", - (unsigned long)f->buf.index, count); - - mutex_unlock(&cam->fileop_mutex); - - return err ? err : count; -} - - -static unsigned int et61x251_poll(struct file *filp, poll_table *wait) -{ - struct et61x251_device *cam = video_drvdata(filp); - struct et61x251_frame_t* f; - unsigned long lock_flags; - unsigned int mask = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return POLLERR; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - goto error; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - goto error; - } - - if (cam->io == IO_NONE) { - if (!et61x251_request_buffers(cam, cam->nreadbuffers, - IO_READ)) { - DBG(1, "poll() failed, not enough memory"); - goto error; - } - cam->io = IO_READ; - cam->stream = STREAM_ON; - } - - if (cam->io == IO_READ) { - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_for_each_entry(f, &cam->outqueue, frame) - f->state = F_UNUSED; - INIT_LIST_HEAD(&cam->outqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - et61x251_queue_unusedframes(cam); - } - - poll_wait(filp, &cam->wait_frame, wait); - - if (!list_empty(&cam->outqueue)) - mask |= POLLIN | POLLRDNORM; - - mutex_unlock(&cam->fileop_mutex); - - return mask; - -error: - mutex_unlock(&cam->fileop_mutex); - return POLLERR; -} - - -static void et61x251_vm_open(struct vm_area_struct* vma) -{ - struct et61x251_frame_t* f = vma->vm_private_data; - f->vma_use_count++; -} - - -static void et61x251_vm_close(struct vm_area_struct* vma) -{ - /* NOTE: buffers are not freed here */ - struct et61x251_frame_t* f = vma->vm_private_data; - f->vma_use_count--; -} - - -static const struct vm_operations_struct et61x251_vm_ops = { - .open = et61x251_vm_open, - .close = et61x251_vm_close, -}; - - -static int et61x251_mmap(struct file* filp, struct vm_area_struct *vma) -{ - struct et61x251_device *cam = video_drvdata(filp); - unsigned long size = vma->vm_end - vma->vm_start, - start = vma->vm_start; - void *pos; - u32 i; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - if (!(vma->vm_flags & (VM_WRITE | VM_READ))) { - mutex_unlock(&cam->fileop_mutex); - return -EACCES; - } - - if (cam->io != IO_MMAP || - size != PAGE_ALIGN(cam->frame[0].buf.length)) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - for (i = 0; i < cam->nbuffers; i++) { - if ((cam->frame[i].buf.m.offset>>PAGE_SHIFT) == vma->vm_pgoff) - break; - } - if (i == cam->nbuffers) { - mutex_unlock(&cam->fileop_mutex); - return -EINVAL; - } - - vma->vm_flags |= VM_IO; - vma->vm_flags |= VM_RESERVED; - - pos = cam->frame[i].bufmem; - while (size > 0) { /* size is page-aligned */ - if (vm_insert_page(vma, start, vmalloc_to_page(pos))) { - mutex_unlock(&cam->fileop_mutex); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - size -= PAGE_SIZE; - } - - vma->vm_ops = &et61x251_vm_ops; - vma->vm_private_data = &cam->frame[i]; - et61x251_vm_open(vma); - - mutex_unlock(&cam->fileop_mutex); - - return 0; -} - -/*****************************************************************************/ - -static int -et61x251_vidioc_querycap(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_capability cap = { - .driver = "et61x251", - .version = LINUX_VERSION_CODE, - .capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING, - }; - - strlcpy(cap.card, cam->v4ldev->name, sizeof(cap.card)); - if (usb_make_path(cam->usbdev, cap.bus_info, sizeof(cap.bus_info)) < 0) - strlcpy(cap.bus_info, dev_name(&cam->usbdev->dev), - sizeof(cap.bus_info)); - - if (copy_to_user(arg, &cap, sizeof(cap))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_enuminput(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_input i; - - if (copy_from_user(&i, arg, sizeof(i))) - return -EFAULT; - - if (i.index) - return -EINVAL; - - memset(&i, 0, sizeof(i)); - strcpy(i.name, "Camera"); - i.type = V4L2_INPUT_TYPE_CAMERA; - i.capabilities = V4L2_IN_CAP_STD; - - if (copy_to_user(arg, &i, sizeof(i))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_input(struct et61x251_device* cam, void __user * arg) -{ - int index = 0; - - if (copy_to_user(arg, &index, sizeof(index))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_input(struct et61x251_device* cam, void __user * arg) -{ - int index; - - if (copy_from_user(&index, arg, sizeof(index))) - return -EFAULT; - - if (index != 0) - return -EINVAL; - - return 0; -} - - -static int -et61x251_vidioc_query_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_queryctrl qc; - u8 i; - - if (copy_from_user(&qc, arg, sizeof(qc))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (qc.id && qc.id == s->qctrl[i].id) { - memcpy(&qc, &(s->qctrl[i]), sizeof(qc)); - if (copy_to_user(arg, &qc, sizeof(qc))) - return -EFAULT; - return 0; - } - - return -EINVAL; -} - - -static int -et61x251_vidioc_g_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - int err = 0; - u8 i; - - if (!s->get_ctrl && !s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - if (!s->get_ctrl) { - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) - if (ctrl.id == s->qctrl[i].id) { - ctrl.value = s->_qctrl[i].default_value; - goto exit; - } - return -EINVAL; - } else - err = s->get_ctrl(cam, &ctrl); - -exit: - if (copy_to_user(arg, &ctrl, sizeof(ctrl))) - return -EFAULT; - - return err; -} - - -static int -et61x251_vidioc_s_ctrl(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_control ctrl; - u8 i; - int err = 0; - - if (!s->set_ctrl) - return -EINVAL; - - if (copy_from_user(&ctrl, arg, sizeof(ctrl))) - return -EFAULT; - - for (i = 0; i < ARRAY_SIZE(s->qctrl); i++) { - if (ctrl.id == s->qctrl[i].id) { - if (s->qctrl[i].flags & V4L2_CTRL_FLAG_DISABLED) - return -EINVAL; - if (ctrl.value < s->qctrl[i].minimum || - ctrl.value > s->qctrl[i].maximum) - return -ERANGE; - ctrl.value -= ctrl.value % s->qctrl[i].step; - break; - } - } - if (i == ARRAY_SIZE(s->qctrl)) - return -EINVAL; - if ((err = s->set_ctrl(cam, &ctrl))) - return err; - - s->_qctrl[i].default_value = ctrl.value; - - return 0; -} - - -static int -et61x251_vidioc_cropcap(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_cropcap* cc = &(cam->sensor.cropcap); - - cc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - cc->pixelaspect.numerator = 1; - cc->pixelaspect.denominator = 1; - - if (copy_to_user(arg, cc, sizeof(*cc))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_crop(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_crop crop = { - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - }; - - memcpy(&(crop.c), &(s->_rect), sizeof(struct v4l2_rect)); - - if (copy_to_user(arg, &crop, sizeof(crop))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_crop(struct et61x251_device* cam, void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_crop crop; - struct v4l2_rect* rect; - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_pix_format* pix_format = &(s->pix_format); - u8 scale; - const enum et61x251_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&crop, arg, sizeof(crop))) - return -EFAULT; - - rect = &(crop.c); - - if (crop.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_CROP failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - /* Preserve R,G or B origin */ - rect->left = (s->_rect.left & 1L) ? rect->left | 1L : rect->left & ~1L; - rect->top = (s->_rect.top & 1L) ? rect->top | 1L : rect->top & ~1L; - - if (rect->width < 16) - rect->width = 16; - if (rect->height < 16) - rect->height = 16; - if (rect->width > bounds->width) - rect->width = bounds->width; - if (rect->height > bounds->height) - rect->height = bounds->height; - if (rect->left < bounds->left) - rect->left = bounds->left; - if (rect->top < bounds->top) - rect->top = bounds->top; - if (rect->left + rect->width > bounds->left + bounds->width) - rect->left = bounds->left+bounds->width - rect->width; - if (rect->top + rect->height > bounds->top + bounds->height) - rect->top = bounds->top+bounds->height - rect->height; - - rect->width &= ~15L; - rect->height &= ~15L; - - if (ET61X251_PRESERVE_IMGSCALE) { - /* Calculate the actual scaling factor */ - u32 a, b; - a = rect->width * rect->height; - b = pix_format->width * pix_format->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } else - scale = 1; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &crop, sizeof(crop))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - et61x251_release_buffers(cam); - - err = et61x251_set_crop(cam, rect); - if (s->set_crop) - err += s->set_crop(cam, rect); - err += et61x251_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - s->pix_format.width = rect->width/scale; - s->pix_format.height = rect->height/scale; - memcpy(&(s->_rect), rect, sizeof(*rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_CROP failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - et61x251_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - et61x251_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_enum_framesizes(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_frmsizeenum frmsize; - - if (copy_from_user(&frmsize, arg, sizeof(frmsize))) - return -EFAULT; - - if (frmsize.index != 0) - return -EINVAL; - - if (frmsize.pixel_format != V4L2_PIX_FMT_ET61X251 && - frmsize.pixel_format != V4L2_PIX_FMT_SBGGR8) - return -EINVAL; - - frmsize.type = V4L2_FRMSIZE_TYPE_STEPWISE; - frmsize.stepwise.min_width = frmsize.stepwise.step_width = 16; - frmsize.stepwise.min_height = frmsize.stepwise.step_height = 16; - frmsize.stepwise.max_width = cam->sensor.cropcap.bounds.width; - frmsize.stepwise.max_height = cam->sensor.cropcap.bounds.height; - memset(&frmsize.reserved, 0, sizeof(frmsize.reserved)); - - if (copy_to_user(arg, &frmsize, sizeof(frmsize))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_enum_fmt(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_fmtdesc fmtd; - - if (copy_from_user(&fmtd, arg, sizeof(fmtd))) - return -EFAULT; - - if (fmtd.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (fmtd.index == 0) { - strcpy(fmtd.description, "bayer rgb"); - fmtd.pixelformat = V4L2_PIX_FMT_SBGGR8; - } else if (fmtd.index == 1) { - strcpy(fmtd.description, "compressed"); - fmtd.pixelformat = V4L2_PIX_FMT_ET61X251; - fmtd.flags = V4L2_FMT_FLAG_COMPRESSED; - } else - return -EINVAL; - - fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memset(&fmtd.reserved, 0, sizeof(fmtd.reserved)); - - if (copy_to_user(arg, &fmtd, sizeof(fmtd))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_g_fmt(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_format format; - struct v4l2_pix_format* pfmt = &(cam->sensor.pix_format); - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - pfmt->colorspace = (pfmt->pixelformat == V4L2_PIX_FMT_ET61X251) ? - 0 : V4L2_COLORSPACE_SRGB; - pfmt->bytesperline = (pfmt->pixelformat==V4L2_PIX_FMT_ET61X251) - ? 0 : (pfmt->width * pfmt->priv) / 8; - pfmt->sizeimage = pfmt->height * ((pfmt->width*pfmt->priv)/8); - pfmt->field = V4L2_FIELD_NONE; - memcpy(&(format.fmt.pix), pfmt, sizeof(*pfmt)); - - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_try_s_fmt(struct et61x251_device* cam, unsigned int cmd, - void __user * arg) -{ - struct et61x251_sensor* s = &cam->sensor; - struct v4l2_format format; - struct v4l2_pix_format* pix; - struct v4l2_pix_format* pfmt = &(s->pix_format); - struct v4l2_rect* bounds = &(s->cropcap.bounds); - struct v4l2_rect rect; - u8 scale; - const enum et61x251_stream_state stream = cam->stream; - const u32 nbuffers = cam->nbuffers; - u32 i; - int err = 0; - - if (copy_from_user(&format, arg, sizeof(format))) - return -EFAULT; - - pix = &(format.fmt.pix); - - if (format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - memcpy(&rect, &(s->_rect), sizeof(rect)); - - { /* calculate the actual scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } - - rect.width = scale * pix->width; - rect.height = scale * pix->height; - - if (rect.width < 16) - rect.width = 16; - if (rect.height < 16) - rect.height = 16; - if (rect.width > bounds->left + bounds->width - rect.left) - rect.width = bounds->left + bounds->width - rect.left; - if (rect.height > bounds->top + bounds->height - rect.top) - rect.height = bounds->top + bounds->height - rect.top; - - rect.width &= ~15L; - rect.height &= ~15L; - - { /* adjust the scaling factor */ - u32 a, b; - a = rect.width * rect.height; - b = pix->width * pix->height; - scale = b ? (u8)((a / b) < 4 ? 1 : 2) : 1; - } - - pix->width = rect.width / scale; - pix->height = rect.height / scale; - - if (pix->pixelformat != V4L2_PIX_FMT_ET61X251 && - pix->pixelformat != V4L2_PIX_FMT_SBGGR8) - pix->pixelformat = pfmt->pixelformat; - pix->priv = pfmt->priv; /* bpp */ - pix->colorspace = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) ? - 0 : V4L2_COLORSPACE_SRGB; - pix->colorspace = pfmt->colorspace; - pix->bytesperline = (pix->pixelformat == V4L2_PIX_FMT_ET61X251) - ? 0 : (pix->width * pix->priv) / 8; - pix->sizeimage = pix->height * ((pix->width * pix->priv) / 8); - pix->field = V4L2_FIELD_NONE; - - if (cmd == VIDIOC_TRY_FMT) { - if (copy_to_user(arg, &format, sizeof(format))) - return -EFAULT; - return 0; - } - - if (cam->module_param.force_munmap) - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_S_FMT failed. " - "Unmap the buffers first."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - if (copy_to_user(arg, &format, sizeof(format))) { - cam->stream = stream; - return -EFAULT; - } - - if (cam->module_param.force_munmap || cam->io == IO_READ) - et61x251_release_buffers(cam); - - err += et61x251_set_pix_format(cam, pix); - err += et61x251_set_crop(cam, &rect); - if (s->set_pix_format) - err += s->set_pix_format(cam, pix); - if (s->set_crop) - err += s->set_crop(cam, &rect); - err += et61x251_set_scale(cam, scale); - - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of hardware problems. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -EIO; - } - - memcpy(pfmt, pix, sizeof(*pix)); - memcpy(&(s->_rect), &rect, sizeof(rect)); - - if ((cam->module_param.force_munmap || cam->io == IO_READ) && - nbuffers != et61x251_request_buffers(cam, nbuffers, cam->io)) { - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_FMT failed because of not enough memory. To " - "use the camera, close and open %s again.", - video_device_node_name(cam->v4ldev)); - return -ENOMEM; - } - - if (cam->io == IO_READ) - et61x251_empty_framequeues(cam); - else if (cam->module_param.force_munmap) - et61x251_requeue_outqueue(cam); - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_g_jpegcomp(struct et61x251_device* cam, void __user * arg) -{ - if (copy_to_user(arg, &cam->compression, - sizeof(cam->compression))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_jpegcomp(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_jpegcompression jc; - const enum et61x251_stream_state stream = cam->stream; - int err = 0; - - if (copy_from_user(&jc, arg, sizeof(jc))) - return -EFAULT; - - if (jc.quality != 0 && jc.quality != 1) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - err += et61x251_set_compression(cam, &jc); - if (err) { /* atomic, no rollback in ioctl() */ - cam->state |= DEV_MISCONFIGURED; - DBG(1, "VIDIOC_S_JPEGCOMP failed because of hardware " - "problems. To use the camera, close and open " - "%s again.", video_device_node_name(cam->v4ldev)); - return -EIO; - } - - cam->compression.quality = jc.quality; - - cam->stream = stream; - - return 0; -} - - -static int -et61x251_vidioc_reqbufs(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_requestbuffers rb; - u32 i; - int err; - - if (copy_from_user(&rb, arg, sizeof(rb))) - return -EFAULT; - - if (rb.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - rb.memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (cam->io == IO_READ) { - DBG(3, "Close and open the device again to choose the mmap " - "I/O method"); - return -EBUSY; - } - - for (i = 0; i < cam->nbuffers; i++) - if (cam->frame[i].vma_use_count) { - DBG(3, "VIDIOC_REQBUFS failed. " - "Previous buffers are still mapped."); - return -EBUSY; - } - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - et61x251_empty_framequeues(cam); - - et61x251_release_buffers(cam); - if (rb.count) - rb.count = et61x251_request_buffers(cam, rb.count, IO_MMAP); - - if (copy_to_user(arg, &rb, sizeof(rb))) { - et61x251_release_buffers(cam); - cam->io = IO_NONE; - return -EFAULT; - } - - cam->io = rb.count ? IO_MMAP : IO_NONE; - - return 0; -} - - -static int -et61x251_vidioc_querybuf(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - memcpy(&b, &cam->frame[b.index].buf, sizeof(b)); - - if (cam->frame[b.index].vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (cam->frame[b.index].state == F_DONE) - b.flags |= V4L2_BUF_FLAG_DONE; - else if (cam->frame[b.index].state != F_UNUSED) - b.flags |= V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_qbuf(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_buffer b; - unsigned long lock_flags; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - b.index >= cam->nbuffers || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->frame[b.index].state != F_UNUSED) - return -EINVAL; - - cam->frame[b.index].state = F_QUEUED; - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - list_add_tail(&cam->frame[b.index].frame, &cam->inqueue); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - PDBGG("Frame #%lu queued", (unsigned long)b.index); - - return 0; -} - - -static int -et61x251_vidioc_dqbuf(struct et61x251_device* cam, struct file* filp, - void __user * arg) -{ - struct v4l2_buffer b; - struct et61x251_frame_t *f; - unsigned long lock_flags; - long timeout; - - if (copy_from_user(&b, arg, sizeof(b))) - return -EFAULT; - - if (b.type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io!= IO_MMAP) - return -EINVAL; - - if (list_empty(&cam->outqueue)) { - if (cam->stream == STREAM_OFF) - return -EINVAL; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - timeout = wait_event_interruptible_timeout - ( cam->wait_frame, - (!list_empty(&cam->outqueue)) || - (cam->state & DEV_DISCONNECTED) || - (cam->state & DEV_MISCONFIGURED), - cam->module_param.frame_timeout * - 1000 * msecs_to_jiffies(1) ); - if (timeout < 0) - return timeout; - if (cam->state & DEV_DISCONNECTED) - return -ENODEV; - if (!timeout || (cam->state & DEV_MISCONFIGURED)) - return -EIO; - } - - spin_lock_irqsave(&cam->queue_lock, lock_flags); - f = list_entry(cam->outqueue.next, struct et61x251_frame_t, frame); - list_del(cam->outqueue.next); - spin_unlock_irqrestore(&cam->queue_lock, lock_flags); - - f->state = F_UNUSED; - - memcpy(&b, &f->buf, sizeof(b)); - if (f->vma_use_count) - b.flags |= V4L2_BUF_FLAG_MAPPED; - - if (copy_to_user(arg, &b, sizeof(b))) - return -EFAULT; - - PDBGG("Frame #%lu dequeued", (unsigned long)f->buf.index); - - return 0; -} - - -static int -et61x251_vidioc_streamon(struct et61x251_device* cam, void __user * arg) -{ - int type; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - cam->stream = STREAM_ON; - - DBG(3, "Stream on"); - - return 0; -} - - -static int -et61x251_vidioc_streamoff(struct et61x251_device* cam, void __user * arg) -{ - int type, err; - - if (copy_from_user(&type, arg, sizeof(type))) - return -EFAULT; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE || cam->io != IO_MMAP) - return -EINVAL; - - if (cam->stream == STREAM_ON) - if ((err = et61x251_stream_interrupt(cam))) - return err; - - et61x251_empty_framequeues(cam); - - DBG(3, "Stream off"); - - return 0; -} - - -static int -et61x251_vidioc_g_parm(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - return 0; -} - - -static int -et61x251_vidioc_s_parm(struct et61x251_device* cam, void __user * arg) -{ - struct v4l2_streamparm sp; - - if (copy_from_user(&sp, arg, sizeof(sp))) - return -EFAULT; - - if (sp.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - sp.parm.capture.extendedmode = 0; - - if (sp.parm.capture.readbuffers == 0) - sp.parm.capture.readbuffers = cam->nreadbuffers; - - if (sp.parm.capture.readbuffers > ET61X251_MAX_FRAMES) - sp.parm.capture.readbuffers = ET61X251_MAX_FRAMES; - - if (copy_to_user(arg, &sp, sizeof(sp))) - return -EFAULT; - - cam->nreadbuffers = sp.parm.capture.readbuffers; - - return 0; -} - - -static long et61x251_ioctl_v4l2(struct file *filp, - unsigned int cmd, void __user *arg) -{ - struct et61x251_device *cam = video_drvdata(filp); - - switch (cmd) { - - case VIDIOC_QUERYCAP: - return et61x251_vidioc_querycap(cam, arg); - - case VIDIOC_ENUMINPUT: - return et61x251_vidioc_enuminput(cam, arg); - - case VIDIOC_G_INPUT: - return et61x251_vidioc_g_input(cam, arg); - - case VIDIOC_S_INPUT: - return et61x251_vidioc_s_input(cam, arg); - - case VIDIOC_QUERYCTRL: - return et61x251_vidioc_query_ctrl(cam, arg); - - case VIDIOC_G_CTRL: - return et61x251_vidioc_g_ctrl(cam, arg); - - case VIDIOC_S_CTRL: - return et61x251_vidioc_s_ctrl(cam, arg); - - case VIDIOC_CROPCAP: - return et61x251_vidioc_cropcap(cam, arg); - - case VIDIOC_G_CROP: - return et61x251_vidioc_g_crop(cam, arg); - - case VIDIOC_S_CROP: - return et61x251_vidioc_s_crop(cam, arg); - - case VIDIOC_ENUM_FMT: - return et61x251_vidioc_enum_fmt(cam, arg); - - case VIDIOC_G_FMT: - return et61x251_vidioc_g_fmt(cam, arg); - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - return et61x251_vidioc_try_s_fmt(cam, cmd, arg); - - case VIDIOC_ENUM_FRAMESIZES: - return et61x251_vidioc_enum_framesizes(cam, arg); - - case VIDIOC_G_JPEGCOMP: - return et61x251_vidioc_g_jpegcomp(cam, arg); - - case VIDIOC_S_JPEGCOMP: - return et61x251_vidioc_s_jpegcomp(cam, arg); - - case VIDIOC_REQBUFS: - return et61x251_vidioc_reqbufs(cam, arg); - - case VIDIOC_QUERYBUF: - return et61x251_vidioc_querybuf(cam, arg); - - case VIDIOC_QBUF: - return et61x251_vidioc_qbuf(cam, arg); - - case VIDIOC_DQBUF: - return et61x251_vidioc_dqbuf(cam, filp, arg); - - case VIDIOC_STREAMON: - return et61x251_vidioc_streamon(cam, arg); - - case VIDIOC_STREAMOFF: - return et61x251_vidioc_streamoff(cam, arg); - - case VIDIOC_G_PARM: - return et61x251_vidioc_g_parm(cam, arg); - - case VIDIOC_S_PARM: - return et61x251_vidioc_s_parm(cam, arg); - - default: - return -ENOTTY; - - } -} - - -static long et61x251_ioctl(struct file *filp, - unsigned int cmd, unsigned long arg) -{ - struct et61x251_device *cam = video_drvdata(filp); - long err = 0; - - if (mutex_lock_interruptible(&cam->fileop_mutex)) - return -ERESTARTSYS; - - if (cam->state & DEV_DISCONNECTED) { - DBG(1, "Device not present"); - mutex_unlock(&cam->fileop_mutex); - return -ENODEV; - } - - if (cam->state & DEV_MISCONFIGURED) { - DBG(1, "The camera is misconfigured. Close and open it " - "again."); - mutex_unlock(&cam->fileop_mutex); - return -EIO; - } - - V4LDBG(3, "et61x251", cmd); - - err = et61x251_ioctl_v4l2(filp, cmd, (void __user *)arg); - - mutex_unlock(&cam->fileop_mutex); - - return err; -} - - -static const struct v4l2_file_operations et61x251_fops = { - .owner = THIS_MODULE, - .open = et61x251_open, - .release = et61x251_release, - .unlocked_ioctl = et61x251_ioctl, - .read = et61x251_read, - .poll = et61x251_poll, - .mmap = et61x251_mmap, -}; - -/*****************************************************************************/ - -/* It exists a single interface only. We do not need to validate anything. */ -static int -et61x251_usb_probe(struct usb_interface* intf, const struct usb_device_id* id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct et61x251_device* cam; - static unsigned int dev_nr; - unsigned int i; - int err = 0; - - if (!(cam = kzalloc(sizeof(struct et61x251_device), GFP_KERNEL))) - return -ENOMEM; - - cam->usbdev = udev; - - if (!(cam->control_buffer = kzalloc(8, GFP_KERNEL))) { - DBG(1, "kmalloc() failed"); - err = -ENOMEM; - goto fail; - } - - if (!(cam->v4ldev = video_device_alloc())) { - DBG(1, "video_device_alloc() failed"); - err = -ENOMEM; - goto fail; - } - - DBG(2, "ET61X[12]51 PC Camera Controller detected " - "(vid/pid 0x%04X:0x%04X)",id->idVendor, id->idProduct); - - for (i = 0; et61x251_sensor_table[i]; i++) { - err = et61x251_sensor_table[i](cam); - if (!err) - break; - } - - if (!err) - DBG(2, "%s image sensor detected", cam->sensor.name); - else { - DBG(1, "No supported image sensor detected"); - err = -ENODEV; - goto fail; - } - - if (et61x251_init(cam)) { - DBG(1, "Initialization failed. I will retry on open()."); - cam->state |= DEV_MISCONFIGURED; - } - - strcpy(cam->v4ldev->name, "ET61X[12]51 PC Camera"); - cam->v4ldev->fops = &et61x251_fops; - cam->v4ldev->release = video_device_release; - cam->v4ldev->parent = &udev->dev; - video_set_drvdata(cam->v4ldev, cam); - - init_completion(&cam->probe); - - err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER, - video_nr[dev_nr]); - if (err) { - DBG(1, "V4L2 device registration failed"); - if (err == -ENFILE && video_nr[dev_nr] == -1) - DBG(1, "Free /dev/videoX node not found"); - video_nr[dev_nr] = -1; - dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - complete_all(&cam->probe); - goto fail; - } - - DBG(2, "V4L2 device registered as %s", - video_device_node_name(cam->v4ldev)); - - cam->module_param.force_munmap = force_munmap[dev_nr]; - cam->module_param.frame_timeout = frame_timeout[dev_nr]; - - dev_nr = (dev_nr < ET61X251_MAX_DEVICES-1) ? dev_nr+1 : 0; - -#ifdef CONFIG_VIDEO_ADV_DEBUG - err = et61x251_create_sysfs(cam); - if (!err) - DBG(2, "Optional device control through 'sysfs' " - "interface ready"); - else - DBG(2, "Failed to create 'sysfs' interface for optional " - "device controlling. Error #%d", err); -#else - DBG(2, "Optional device control through 'sysfs' interface disabled"); - DBG(3, "Compile the kernel with the 'CONFIG_VIDEO_ADV_DEBUG' " - "configuration option to enable it."); -#endif - - usb_set_intfdata(intf, cam); - kref_init(&cam->kref); - usb_get_dev(cam->usbdev); - - complete_all(&cam->probe); - - return 0; - -fail: - if (cam) { - kfree(cam->control_buffer); - if (cam->v4ldev) - video_device_release(cam->v4ldev); - kfree(cam); - } - return err; -} - - -static void et61x251_usb_disconnect(struct usb_interface* intf) -{ - struct et61x251_device* cam; - - down_write(&et61x251_dev_lock); - - cam = usb_get_intfdata(intf); - - DBG(2, "Disconnecting %s...", cam->v4ldev->name); - - if (cam->users) { - DBG(2, "Device %s is open! Deregistration and memory " - "deallocation are deferred.", - video_device_node_name(cam->v4ldev)); - cam->state |= DEV_MISCONFIGURED; - et61x251_stop_transfer(cam); - cam->state |= DEV_DISCONNECTED; - wake_up_interruptible(&cam->wait_frame); - wake_up(&cam->wait_stream); - } else - cam->state |= DEV_DISCONNECTED; - - wake_up_interruptible_all(&cam->wait_open); - - kref_put(&cam->kref, et61x251_release_resources); - - up_write(&et61x251_dev_lock); -} - - -static struct usb_driver et61x251_usb_driver = { - .name = "et61x251", - .id_table = et61x251_id_table, - .probe = et61x251_usb_probe, - .disconnect = et61x251_usb_disconnect, -}; - -module_usb_driver(et61x251_usb_driver); diff --git a/drivers/media/video/et61x251/et61x251_sensor.h b/drivers/media/video/et61x251/et61x251_sensor.h deleted file mode 100644 index 71a03148cb09..000000000000 --- a/drivers/media/video/et61x251/et61x251_sensor.h +++ /dev/null @@ -1,108 +0,0 @@ -/*************************************************************************** - * API for image sensors connected to ET61X[12]51 PC Camera Controllers * - * * - * Copyright (C) 2006-2007 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 * - * (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 _ET61X251_SENSOR_H_ -#define _ET61X251_SENSOR_H_ - -#include -#include -#include -#include -#include -#include - -struct et61x251_device; -struct et61x251_sensor; - -/*****************************************************************************/ - -extern int et61x251_probe_tas5130d1b(struct et61x251_device* cam); - -#define ET61X251_SENSOR_TABLE \ -/* Weak detections must go at the end of the list */ \ -static int (*et61x251_sensor_table[])(struct et61x251_device*) = { \ - &et61x251_probe_tas5130d1b, \ - NULL, \ -}; - -extern struct et61x251_device* -et61x251_match_id(struct et61x251_device* cam, const struct usb_device_id *id); - -extern void -et61x251_attach_sensor(struct et61x251_device* cam, - const struct et61x251_sensor* sensor); - -/*****************************************************************************/ - -extern int et61x251_write_reg(struct et61x251_device*, u8 value, u16 index); -extern int et61x251_i2c_raw_write(struct et61x251_device*, u8 n, u8 data1, - u8 data2, u8 data3, u8 data4, u8 data5, - u8 data6, u8 data7, u8 data8, u8 address); - -/*****************************************************************************/ - -enum et61x251_i2c_sysfs_ops { - ET61X251_I2C_READ = 0x01, - ET61X251_I2C_WRITE = 0x02, -}; - -enum et61x251_i2c_interface { - ET61X251_I2C_2WIRES, - ET61X251_I2C_3WIRES, -}; - -/* Repeat start condition when RSTA is high */ -enum et61x251_i2c_rsta { - ET61X251_I2C_RSTA_STOP = 0x00, /* stop then start */ - ET61X251_I2C_RSTA_REPEAT = 0x01, /* repeat start */ -}; - -#define ET61X251_MAX_CTRLS (V4L2_CID_LASTP1-V4L2_CID_BASE+10) - -struct et61x251_sensor { - char name[32]; - - enum et61x251_i2c_sysfs_ops sysfs_ops; - - enum et61x251_i2c_interface interface; - u8 i2c_slave_id; - enum et61x251_i2c_rsta rsta; - struct v4l2_rect active_pixel; /* left and top define FVSX and FVSY */ - - struct v4l2_queryctrl qctrl[ET61X251_MAX_CTRLS]; - struct v4l2_cropcap cropcap; - struct v4l2_pix_format pix_format; - - int (*init)(struct et61x251_device* cam); - int (*get_ctrl)(struct et61x251_device* cam, - struct v4l2_control* ctrl); - int (*set_ctrl)(struct et61x251_device* cam, - const struct v4l2_control* ctrl); - int (*set_crop)(struct et61x251_device* cam, - const struct v4l2_rect* rect); - int (*set_pix_format)(struct et61x251_device* cam, - const struct v4l2_pix_format* pix); - - /* Private */ - struct v4l2_queryctrl _qctrl[ET61X251_MAX_CTRLS]; - struct v4l2_rect _rect; -}; - -#endif /* _ET61X251_SENSOR_H_ */ diff --git a/drivers/media/video/et61x251/et61x251_tas5130d1b.c b/drivers/media/video/et61x251/et61x251_tas5130d1b.c deleted file mode 100644 index ced2e167935d..000000000000 --- a/drivers/media/video/et61x251/et61x251_tas5130d1b.c +++ /dev/null @@ -1,143 +0,0 @@ -/*************************************************************************** - * Plug-in for TAS5130D1B image sensor connected to the ET61X[12]51 * - * PC Camera Controllers * - * * - * Copyright (C) 2006-2007 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 * - * (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. * - ***************************************************************************/ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include "et61x251_sensor.h" - - -static int tas5130d1b_init(struct et61x251_device* cam) -{ - int err = 0; - - err += et61x251_write_reg(cam, 0x14, 0x01); - err += et61x251_write_reg(cam, 0x1b, 0x02); - err += et61x251_write_reg(cam, 0x02, 0x12); - err += et61x251_write_reg(cam, 0x0e, 0x60); - err += et61x251_write_reg(cam, 0x80, 0x61); - err += et61x251_write_reg(cam, 0xf0, 0x62); - err += et61x251_write_reg(cam, 0x03, 0x63); - err += et61x251_write_reg(cam, 0x14, 0x64); - err += et61x251_write_reg(cam, 0xf4, 0x65); - err += et61x251_write_reg(cam, 0x01, 0x66); - err += et61x251_write_reg(cam, 0x05, 0x67); - err += et61x251_write_reg(cam, 0x8f, 0x68); - err += et61x251_write_reg(cam, 0x0f, 0x8d); - err += et61x251_write_reg(cam, 0x08, 0x8e); - - return err; -} - - -static int tas5130d1b_set_ctrl(struct et61x251_device* cam, - const struct v4l2_control* ctrl) -{ - int err = 0; - - switch (ctrl->id) { - case V4L2_CID_GAIN: - err += et61x251_i2c_raw_write(cam, 2, 0x20, - 0xf6-ctrl->value, 0, 0, 0, - 0, 0, 0, 0); - break; - case V4L2_CID_EXPOSURE: - err += et61x251_i2c_raw_write(cam, 2, 0x40, - 0x47-ctrl->value, 0, 0, 0, - 0, 0, 0, 0); - break; - default: - return -EINVAL; - } - - return err ? -EIO : 0; -} - - -static const struct et61x251_sensor tas5130d1b = { - .name = "TAS5130D1B", - .interface = ET61X251_I2C_3WIRES, - .rsta = ET61X251_I2C_RSTA_STOP, - .active_pixel = { - .left = 106, - .top = 13, - }, - .init = &tas5130d1b_init, - .qctrl = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "global gain", - .minimum = 0x00, - .maximum = 0xf6, - .step = 0x02, - .default_value = 0x0d, - .flags = 0, - }, - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0x47, - .step = 0x01, - .default_value = 0x23, - .flags = 0, - }, - }, - .set_ctrl = &tas5130d1b_set_ctrl, - .cropcap = { - .bounds = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - .defrect = { - .left = 0, - .top = 0, - .width = 640, - .height = 480, - }, - }, - .pix_format = { - .width = 640, - .height = 480, - .pixelformat = V4L2_PIX_FMT_SBGGR8, - .priv = 8, - }, -}; - - -int et61x251_probe_tas5130d1b(struct et61x251_device* cam) -{ - const struct usb_device_id tas5130d1b_id_table[] = { - { USB_DEVICE(0x102c, 0x6251), }, - { } - }; - - /* Sensor detection is based on USB pid/vid */ - if (!et61x251_match_id(cam, tas5130d1b_id_table)) - return -ENODEV; - - et61x251_attach_sensor(cam, &tas5130d1b); - - return 0; -} diff --git a/drivers/media/video/fsl-viu.c b/drivers/media/video/fsl-viu.c index 27e3e0c0b219..777486f7cadb 100644 --- a/drivers/media/video/fsl-viu.c +++ b/drivers/media/video/fsl-viu.c @@ -1544,6 +1544,10 @@ static int __devinit viu_of_probe(struct platform_device *op) /* initialize locks */ mutex_init(&viu_dev->lock); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &viu_dev->vdev->flags); viu_dev->vdev->lock = &viu_dev->lock; spin_lock_init(&viu_dev->slock); diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile index 79ebe46e1ad7..c901da0bd657 100644 --- a/drivers/media/video/gspca/Makefile +++ b/drivers/media/video/gspca/Makefile @@ -43,7 +43,7 @@ obj-$(CONFIG_USB_GSPCA_VICAM) += gspca_vicam.o obj-$(CONFIG_USB_GSPCA_XIRLINK_CIT) += gspca_xirlink_cit.o obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o -gspca_main-objs := gspca.o +gspca_main-objs := gspca.o autogain_functions.o gspca_benq-objs := benq.o gspca_conex-objs := conex.o gspca_cpia1-objs := cpia1.o diff --git a/drivers/media/video/gspca/autogain_functions.c b/drivers/media/video/gspca/autogain_functions.c new file mode 100644 index 000000000000..67db674bb044 --- /dev/null +++ b/drivers/media/video/gspca/autogain_functions.c @@ -0,0 +1,178 @@ +/* + * Functions for auto gain. + * + * Copyright (C) 2010-2012 Hans de Goede + * + * 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 + */ +#include "gspca.h" + +/* auto gain and exposure algorithm based on the knee algorithm described here: + http://ytse.tricolour.net/docs/LowLightOptimization.html + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone, + int gain_knee, + int exposure_knee) +{ + s32 gain, orig_gain, exposure, orig_exposure; + int i, steps, retval = 0; + + if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) + return 0; + + orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = abs(desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + for (i = 0; i < steps; i++) { + if (avg_lum > desired_avg_lum) { + if (gain > gain_knee) + gain--; + else if (exposure > exposure_knee) + exposure--; + else if (gain > gspca_dev->gain->default_value) + gain--; + else if (exposure > gspca_dev->exposure->minimum) + exposure--; + else if (gain > gspca_dev->gain->minimum) + gain--; + else + break; + } else { + if (gain < gspca_dev->gain->default_value) + gain++; + else if (exposure < exposure_knee) + exposure++; + else if (gain < gain_knee) + gain++; + else if (exposure < gspca_dev->exposure->maximum) + exposure++; + else if (gain < gspca_dev->gain->maximum) + gain++; + else + break; + } + } + + if (gain != orig_gain) { + v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); + retval = 1; + } + if (exposure != orig_exposure) { + v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +EXPORT_SYMBOL(gspca_expo_autogain); + +/* Autogain + exposure algorithm for cameras with a coarse exposure control + (usually this means we can only control the clockdiv to change exposure) + As changing the clockdiv so that the fps drops from 30 to 15 fps for + example, will lead to a huge exposure change (it effectively doubles), + this algorithm normally tries to only adjust the gain (between 40 and + 80 %) and if that does not help, only then changes exposure. This leads + to a much more stable image then using the knee algorithm which at + certain points of the knee graph will only try to adjust exposure, + which leads to oscilating as one exposure step is huge. + + Returns 0 if no changes were made, 1 if the gain and or exposure settings + where changed. */ +int gspca_coarse_grained_expo_autogain( + struct gspca_dev *gspca_dev, + int avg_lum, + int desired_avg_lum, + int deadzone) +{ + s32 gain_low, gain_high, gain, orig_gain, exposure, orig_exposure; + int steps, retval = 0; + + if (v4l2_ctrl_g_ctrl(gspca_dev->autogain) == 0) + return 0; + + orig_gain = gain = v4l2_ctrl_g_ctrl(gspca_dev->gain); + orig_exposure = exposure = v4l2_ctrl_g_ctrl(gspca_dev->exposure); + + gain_low = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + 5 * 2 + gspca_dev->gain->minimum; + gain_high = (gspca_dev->gain->maximum - gspca_dev->gain->minimum) / + 5 * 4 + gspca_dev->gain->minimum; + + /* If we are of a multiple of deadzone, do multiple steps to reach the + desired lumination fast (with the risc of a slight overshoot) */ + steps = (desired_avg_lum - avg_lum) / deadzone; + + PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", + avg_lum, desired_avg_lum, steps); + + if ((gain + steps) > gain_high && + exposure < gspca_dev->exposure->maximum) { + gain = gain_high; + gspca_dev->exp_too_low_cnt++; + gspca_dev->exp_too_high_cnt = 0; + } else if ((gain + steps) < gain_low && + exposure > gspca_dev->exposure->minimum) { + gain = gain_low; + gspca_dev->exp_too_high_cnt++; + gspca_dev->exp_too_low_cnt = 0; + } else { + gain += steps; + if (gain > gspca_dev->gain->maximum) + gain = gspca_dev->gain->maximum; + else if (gain < gspca_dev->gain->minimum) + gain = gspca_dev->gain->minimum; + gspca_dev->exp_too_high_cnt = 0; + gspca_dev->exp_too_low_cnt = 0; + } + + if (gspca_dev->exp_too_high_cnt > 3) { + exposure--; + gspca_dev->exp_too_high_cnt = 0; + } else if (gspca_dev->exp_too_low_cnt > 3) { + exposure++; + gspca_dev->exp_too_low_cnt = 0; + } + + if (gain != orig_gain) { + v4l2_ctrl_s_ctrl(gspca_dev->gain, gain); + retval = 1; + } + if (exposure != orig_exposure) { + v4l2_ctrl_s_ctrl(gspca_dev->exposure, exposure); + retval = 1; + } + + if (retval) + PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d", + gain, exposure); + return retval; +} +EXPORT_SYMBOL(gspca_coarse_grained_expo_autogain); diff --git a/drivers/media/video/gspca/autogain_functions.h b/drivers/media/video/gspca/autogain_functions.h index 46777eee678b..d625eafe63eb 100644 --- a/drivers/media/video/gspca/autogain_functions.h +++ b/drivers/media/video/gspca/autogain_functions.h @@ -18,6 +18,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#ifdef WANT_REGULAR_AUTOGAIN /* auto gain and exposure algorithm based on the knee algorithm described here: http://ytse.tricolour.net/docs/LowLightOptimization.html @@ -91,7 +92,9 @@ static inline int auto_gain_n_exposure( gain, exposure); return retval; } +#endif +#ifdef WANT_COARSE_EXPO_AUTOGAIN /* Autogain + exposure algorithm for cameras with a coarse exposure control (usually this means we can only control the clockdiv to change exposure) As changing the clockdiv so that the fps drops from 30 to 15 fps for @@ -103,7 +106,7 @@ static inline int auto_gain_n_exposure( which leads to oscilating as one exposure step is huge. Note this assumes that the sd struct for the cam in question has - exp_too_high_cnt and exp_too_high_cnt int members for use by this function. + exp_too_low_cnt and exp_too_high_cnt int members for use by this function. Returns 0 if no changes were made, 1 if the gain and or exposure settings where changed. */ @@ -177,3 +180,4 @@ static inline int coarse_grained_expo_autogain( gain, exposure); return retval; } +#endif diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c index ea17b5d94ea4..f39fee0fd10f 100644 --- a/drivers/media/video/gspca/conex.c +++ b/drivers/media/video/gspca/conex.c @@ -306,7 +306,7 @@ static void cx_sensor(struct gspca_dev*gspca_dev) reg_w(gspca_dev, 0x0020, reg20, 8); reg_w(gspca_dev, 0x0028, reg28, 8); - reg_w(gspca_dev, 0x0010, reg10, 8); + reg_w(gspca_dev, 0x0010, reg10, 2); reg_w_val(gspca_dev, 0x0092, 0x03); switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { @@ -326,7 +326,7 @@ static void cx_sensor(struct gspca_dev*gspca_dev) } reg_w(gspca_dev, 0x007b, reg7b, 6); reg_w_val(gspca_dev, 0x00f8, 0x00); - reg_w(gspca_dev, 0x0010, reg10, 8); + reg_w(gspca_dev, 0x0010, reg10, 2); reg_w_val(gspca_dev, 0x0098, 0x41); for (i = 0; i < 11; i++) { if (i == 3 || i == 5 || i == 8) diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c index 0107513cd728..6e26c93b4656 100644 --- a/drivers/media/video/gspca/finepix.c +++ b/drivers/media/video/gspca/finepix.c @@ -94,7 +94,11 @@ static void dostream(struct work_struct *work) /* loop reading a frame */ again: - while (gspca_dev->present && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif /* request a frame */ mutex_lock(&gspca_dev->usb_lock); @@ -102,7 +106,11 @@ again: mutex_unlock(&gspca_dev->usb_lock); if (ret < 0) break; - if (!gspca_dev->present || !gspca_dev->streaming) +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) break; /* the frame comes in parts */ @@ -117,7 +125,11 @@ again: * error. Just restart. */ goto again; } - if (!gspca_dev->present || !gspca_dev->streaming) +#ifdef CONFIG_PM + if (gspca_dev->frozen) + goto out; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) goto out; if (len < FPIX_MAX_TRANSFER || (data[len - 2] == 0xff && diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c index c84e26006fc3..c549574c1c7e 100644 --- a/drivers/media/video/gspca/gl860/gl860.c +++ b/drivers/media/video/gspca/gl860/gl860.c @@ -405,6 +405,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + if (!sd->gspca_dev.present) + return; + return sd->dev_post_unset_alt(gspca_dev); } diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c index ca5a2b139d0b..137166d73945 100644 --- a/drivers/media/video/gspca/gspca.c +++ b/drivers/media/video/gspca/gspca.c @@ -38,6 +38,9 @@ #include #include #include +#include +#include +#include #include "gspca.h" @@ -592,16 +595,13 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev) static void gspca_stream_off(struct gspca_dev *gspca_dev) { gspca_dev->streaming = 0; - if (gspca_dev->present) { - if (gspca_dev->sd_desc->stopN) - gspca_dev->sd_desc->stopN(gspca_dev); - destroy_urbs(gspca_dev); - gspca_input_destroy_urb(gspca_dev); - gspca_set_alt0(gspca_dev); - gspca_input_create_urb(gspca_dev); - } - - /* always call stop0 to free the subdriver's resources */ + gspca_dev->usb_err = 0; + if (gspca_dev->sd_desc->stopN) + gspca_dev->sd_desc->stopN(gspca_dev); + destroy_urbs(gspca_dev); + gspca_input_destroy_urb(gspca_dev); + gspca_set_alt0(gspca_dev); + gspca_input_create_urb(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); PDEBUG(D_STREAM, "stream off OK"); @@ -847,14 +847,6 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) struct ep_tb_s ep_tb[MAX_ALT]; int n, ret, xfer, alt, alt_idx; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - - if (!gspca_dev->present) { - ret = -ENODEV; - goto unlock; - } - /* reset the streaming variables */ gspca_dev->image = NULL; gspca_dev->image_len = 0; @@ -869,7 +861,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) if (gspca_dev->sd_desc->isoc_init) { ret = gspca_dev->sd_desc->isoc_init(gspca_dev); if (ret < 0) - goto unlock; + return ret; } xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK : USB_ENDPOINT_XFER_ISOC; @@ -880,8 +872,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer); if (ep == NULL) { pr_err("bad altsetting %d\n", gspca_dev->alt); - ret = -EIO; - goto out; + return -EIO; } ep_tb[0].alt = gspca_dev->alt; alt_idx = 1; @@ -892,8 +883,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev) alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb); if (alt_idx <= 0) { pr_err("no transfer endpoint found\n"); - ret = -EIO; - goto unlock; + return -EIO; } } @@ -988,8 +978,6 @@ retry: } out: gspca_input_create_urb(gspca_dev); -unlock: - mutex_unlock(&gspca_dev->usb_lock); return ret; } @@ -1006,6 +994,8 @@ static void gspca_set_default_mode(struct gspca_dev *gspca_dev) /* set the current control values to their default values * which may have changed in sd_init() */ + /* does nothing if ctrl_handler == NULL */ + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); ctrl = gspca_dev->cam.ctrls; if (ctrl != NULL) { for (i = 0; @@ -1057,77 +1047,50 @@ static int gspca_get_mode(struct gspca_dev *gspca_dev, static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; if (!gspca_dev->sd_desc->get_register) - return -EINVAL; + return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_register(gspca_dev, reg); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->get_register(gspca_dev, reg); } static int vidioc_s_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { - int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; if (!gspca_dev->sd_desc->set_register) - return -EINVAL; + return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->set_register(gspca_dev, reg); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->set_register(gspca_dev, reg); } #endif static int vidioc_g_chip_ident(struct file *file, void *priv, struct v4l2_dbg_chip_ident *chip) { - int ret; - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_chip_ident) - return -EINVAL; + return -ENOTTY; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - - return ret; + return gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip); } static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i, j, index; __u32 fmt_tb[8]; @@ -1169,7 +1132,7 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int mode; mode = gspca_dev->curr_mode; @@ -1214,7 +1177,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; ret = try_fmt_vid_cap(gspca_dev, fmt); @@ -1226,7 +1189,7 @@ static int vidioc_try_fmt_vid_cap(struct file *file, static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) @@ -1265,7 +1228,7 @@ out: static int vidioc_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i; __u32 index = 0; @@ -1291,7 +1254,7 @@ static int vidioc_enum_framesizes(struct file *file, void *priv, static int vidioc_enum_frameintervals(struct file *filp, void *priv, struct v4l2_frmivalenum *fival) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); int mode = wxh_to_mode(gspca_dev, fival->width, fival->height); __u32 i; @@ -1316,31 +1279,30 @@ static int vidioc_enum_frameintervals(struct file *filp, void *priv, return -EINVAL; } -static void gspca_release(struct video_device *vfd) +static void gspca_release(struct v4l2_device *v4l2_device) { - struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev); + struct gspca_dev *gspca_dev = + container_of(v4l2_device, struct gspca_dev, v4l2_dev); PDEBUG(D_PROBE, "%s released", video_device_node_name(&gspca_dev->vdev)); + v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); + v4l2_device_unregister(&gspca_dev->v4l2_dev); kfree(gspca_dev->usb_buf); kfree(gspca_dev); } static int dev_open(struct file *file) { - struct gspca_dev *gspca_dev; + struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] open", current->comm); - gspca_dev = (struct gspca_dev *) video_devdata(file); - if (!gspca_dev->present) - return -ENODEV; /* protect the subdriver against rmmod */ if (!try_module_get(gspca_dev->module)) return -ENODEV; - file->private_data = gspca_dev; #ifdef GSPCA_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) @@ -1350,49 +1312,44 @@ static int dev_open(struct file *file) gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG); #endif - return 0; + return v4l2_fh_open(file); } static int dev_close(struct file *file) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] close", current->comm); - if (mutex_lock_interruptible(&gspca_dev->queue_lock)) + + /* Needed for gspca_stream_off, always lock before queue_lock! */ + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; + if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { + mutex_unlock(&gspca_dev->usb_lock); + return -ERESTARTSYS; + } + /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { - if (gspca_dev->streaming) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; + if (gspca_dev->streaming) gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } frame_free(gspca_dev); } - file->private_data = NULL; module_put(gspca_dev->module); mutex_unlock(&gspca_dev->queue_lock); + mutex_unlock(&gspca_dev->usb_lock); PDEBUG(D_STREAM, "close done"); - return 0; + return v4l2_fh_release(file); } static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { - struct gspca_dev *gspca_dev = priv; - int ret; + struct gspca_dev *gspca_dev = video_drvdata(file); - /* protect the access to the usb device */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } strlcpy((char *) cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver); if (gspca_dev->dev->product != NULL) { @@ -1406,13 +1363,11 @@ static int vidioc_querycap(struct file *file, void *priv, } usb_make_path(gspca_dev->dev, (char *) cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; - ret = 0; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + return 0; } static int get_ctrl(struct gspca_dev *gspca_dev, @@ -1435,7 +1390,7 @@ static int get_ctrl(struct gspca_dev *gspca_dev, static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; struct gspca_ctrl *gspca_ctrl; int i, idx; @@ -1478,10 +1433,10 @@ static int vidioc_queryctrl(struct file *file, void *priv, static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; struct gspca_ctrl *gspca_ctrl; - int idx, ret; + int idx; idx = get_ctrl(gspca_dev, ctrl->id); if (idx < 0) @@ -1501,74 +1456,52 @@ static int vidioc_s_ctrl(struct file *file, void *priv, return -ERANGE; } PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } gspca_dev->usb_err = 0; - if (ctrls->set != NULL) { - ret = ctrls->set(gspca_dev, ctrl->value); - goto out; - } + if (ctrls->set != NULL) + return ctrls->set(gspca_dev, ctrl->value); if (gspca_ctrl != NULL) { gspca_ctrl->val = ctrl->value; if (ctrls->set_control != NULL && gspca_dev->streaming) ctrls->set_control(gspca_dev); } - ret = gspca_dev->usb_err; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->usb_err; } static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); const struct ctrl *ctrls; - int idx, ret; + int idx; idx = get_ctrl(gspca_dev, ctrl->id); if (idx < 0) return -EINVAL; ctrls = &gspca_dev->sd_desc->ctrls[idx]; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } gspca_dev->usb_err = 0; - if (ctrls->get != NULL) { - ret = ctrls->get(gspca_dev, &ctrl->value); - goto out; - } + if (ctrls->get != NULL) + return ctrls->get(gspca_dev, &ctrl->value); if (gspca_dev->cam.ctrls != NULL) ctrl->value = gspca_dev->cam.ctrls[idx].val; - ret = 0; -out: - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return 0; } static int vidioc_querymenu(struct file *file, void *priv, struct v4l2_querymenu *qmenu) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->querymenu) - return -EINVAL; + return -ENOTTY; return gspca_dev->sd_desc->querymenu(gspca_dev, qmenu); } static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); if (input->index != 0) return -EINVAL; @@ -1595,7 +1528,7 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i) static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int i, ret = 0, streaming; i = rb->memory; /* (avoid compilation warning) */ @@ -1635,10 +1568,7 @@ static int vidioc_reqbufs(struct file *file, void *priv, /* stop streaming */ streaming = gspca_dev->streaming; if (streaming) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); /* Don't restart the stream when switching from read * to mmap mode */ @@ -1666,7 +1596,7 @@ out: static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; if (v4l2_buf->index < 0 @@ -1681,7 +1611,7 @@ static int vidioc_querybuf(struct file *file, void *priv, static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type buf_type) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1722,7 +1652,7 @@ out: static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type buf_type) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) @@ -1743,13 +1673,7 @@ static int vidioc_streamoff(struct file *file, void *priv, } /* stop streaming */ - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) { - ret = -ERESTARTSYS; - goto out; - } - gspca_dev->usb_err = 0; gspca_stream_off(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); /* In case another thread is waiting in dqbuf */ wake_up_interruptible(&gspca_dev->wq); @@ -1766,71 +1690,44 @@ out: static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { - struct gspca_dev *gspca_dev = priv; - int ret; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->get_jcomp) - return -EINVAL; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; + return -ENOTTY; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); } static int vidioc_s_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { - struct gspca_dev *gspca_dev = priv; - int ret; + struct gspca_dev *gspca_dev = video_drvdata(file); if (!gspca_dev->sd_desc->set_jcomp) - return -EINVAL; - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; + return -ENOTTY; gspca_dev->usb_err = 0; - if (gspca_dev->present) - ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); - else - ret = -ENODEV; - mutex_unlock(&gspca_dev->usb_lock); - return ret; + return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); } static int vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); parm->parm.capture.readbuffers = gspca_dev->nbufread; if (gspca_dev->sd_desc->get_streamparm) { - int ret; - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (gspca_dev->present) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); - ret = gspca_dev->usb_err; - } else { - ret = -ENODEV; - } - mutex_unlock(&gspca_dev->usb_lock); - return ret; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; } - return 0; } static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(filp); int n; n = parm->parm.capture.readbuffers; @@ -1840,19 +1737,9 @@ static int vidioc_s_parm(struct file *filp, void *priv, gspca_dev->nbufread = n; if (gspca_dev->sd_desc->set_streamparm) { - int ret; - - if (mutex_lock_interruptible(&gspca_dev->usb_lock)) - return -ERESTARTSYS; - if (gspca_dev->present) { - gspca_dev->usb_err = 0; - gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); - ret = gspca_dev->usb_err; - } else { - ret = -ENODEV; - } - mutex_unlock(&gspca_dev->usb_lock); - return ret; + gspca_dev->usb_err = 0; + gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); + return gspca_dev->usb_err; } return 0; @@ -1860,7 +1747,7 @@ static int vidioc_s_parm(struct file *filp, void *priv, static int dev_mmap(struct file *file, struct vm_area_struct *vma) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; struct page *page; unsigned long addr, start, size; @@ -1872,10 +1759,6 @@ static int dev_mmap(struct file *file, struct vm_area_struct *vma) if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; - if (!gspca_dev->present) { - ret = -ENODEV; - goto out; - } if (gspca_dev->capt_file != file) { ret = -EINVAL; goto out; @@ -1963,7 +1846,7 @@ static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; int i, j, ret; @@ -2003,14 +1886,6 @@ static int vidioc_dqbuf(struct file *file, void *priv, gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; - if (gspca_dev->sd_desc->dq_callback) { - mutex_lock(&gspca_dev->usb_lock); - gspca_dev->usb_err = 0; - if (gspca_dev->present) - gspca_dev->sd_desc->dq_callback(gspca_dev); - mutex_unlock(&gspca_dev->usb_lock); - } - frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); PDEBUG(D_FRAM, "dqbuf %d", j); @@ -2027,6 +1902,15 @@ static int vidioc_dqbuf(struct file *file, void *priv, } out: mutex_unlock(&gspca_dev->queue_lock); + + if (ret == 0 && gspca_dev->sd_desc->dq_callback) { + mutex_lock(&gspca_dev->usb_lock); + gspca_dev->usb_err = 0; + if (gspca_dev->present) + gspca_dev->sd_desc->dq_callback(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + } + return ret; } @@ -2039,7 +1923,7 @@ out: static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { - struct gspca_dev *gspca_dev = priv; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; int i, index, ret; @@ -2098,6 +1982,10 @@ static int read_alloc(struct gspca_dev *gspca_dev, int i, ret; PDEBUG(D_STREAM, "read alloc"); + + if (mutex_lock_interruptible(&gspca_dev->usb_lock)) + return -ERESTARTSYS; + if (gspca_dev->nframes == 0) { struct v4l2_requestbuffers rb; @@ -2108,7 +1996,7 @@ static int read_alloc(struct gspca_dev *gspca_dev, ret = vidioc_reqbufs(file, gspca_dev, &rb); if (ret != 0) { PDEBUG(D_STREAM, "read reqbuf err %d", ret); - return ret; + goto out; } memset(&v4l2_buf, 0, sizeof v4l2_buf); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; @@ -2118,61 +2006,69 @@ static int read_alloc(struct gspca_dev *gspca_dev, ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); if (ret != 0) { PDEBUG(D_STREAM, "read qbuf err: %d", ret); - return ret; + goto out; } } - gspca_dev->memory = GSPCA_MEMORY_READ; } /* start streaming */ ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); if (ret != 0) PDEBUG(D_STREAM, "read streamon err %d", ret); +out: + mutex_unlock(&gspca_dev->usb_lock); return ret; } static unsigned int dev_poll(struct file *file, poll_table *wait) { - struct gspca_dev *gspca_dev = file->private_data; - int ret; + struct gspca_dev *gspca_dev = video_drvdata(file); + unsigned long req_events = poll_requested_events(wait); + int ret = 0; PDEBUG(D_FRAM, "poll"); - poll_wait(file, &gspca_dev->wq, wait); + if (req_events & POLLPRI) + ret |= v4l2_ctrl_poll(file, wait); - /* if reqbufs is not done, the user would use read() */ - if (gspca_dev->memory == GSPCA_MEMORY_NO) { - ret = read_alloc(gspca_dev, file); - if (ret != 0) - return POLLERR; + if (req_events & (POLLIN | POLLRDNORM)) { + /* if reqbufs is not done, the user would use read() */ + if (gspca_dev->memory == GSPCA_MEMORY_NO) { + if (read_alloc(gspca_dev, file) != 0) { + ret |= POLLERR; + goto out; + } + } + + poll_wait(file, &gspca_dev->wq, wait); + + /* check if an image has been received */ + if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { + ret |= POLLERR; + goto out; + } + if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) + ret |= POLLIN | POLLRDNORM; + mutex_unlock(&gspca_dev->queue_lock); } - if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) - return POLLERR; - - /* check if an image has been received */ - if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) - ret = POLLIN | POLLRDNORM; /* yes */ - else - ret = 0; - mutex_unlock(&gspca_dev->queue_lock); +out: if (!gspca_dev->present) - return POLLHUP; + ret |= POLLHUP; + return ret; } static ssize_t dev_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { - struct gspca_dev *gspca_dev = file->private_data; + struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; struct v4l2_buffer v4l2_buf; struct timeval timestamp; int n, ret, ret2; PDEBUG(D_FRAM, "read (%zd)", count); - if (!gspca_dev->present) - return -ENODEV; if (gspca_dev->memory == GSPCA_MEMORY_NO) { /* first time ? */ ret = read_alloc(gspca_dev, file); if (ret != 0) @@ -2266,13 +2162,15 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_s_register = vidioc_s_register, #endif .vidioc_g_chip_ident = vidioc_g_chip_ident, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static const struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, - .release = gspca_release, + .release = video_device_release_empty, /* We use v4l2_dev.release */ }; /* initialize the controls */ @@ -2344,9 +2242,24 @@ int gspca_dev_probe2(struct usb_interface *intf, } } + gspca_dev->v4l2_dev.release = gspca_release; + ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); + if (ret) + goto out; gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ + gspca_dev->vdev = gspca_template; + gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; + video_set_drvdata(&gspca_dev->vdev, gspca_dev); + set_bit(V4L2_FL_USE_FH_PRIO, &gspca_dev->vdev.flags); + gspca_dev->module = module; + gspca_dev->present = 1; + + mutex_init(&gspca_dev->usb_lock); + gspca_dev->vdev.lock = &gspca_dev->usb_lock; + mutex_init(&gspca_dev->queue_lock); + init_waitqueue_head(&gspca_dev->wq); /* configure the subdriver and initialize the USB device */ ret = sd_desc->config(gspca_dev, id); @@ -2355,6 +2268,10 @@ int gspca_dev_probe2(struct usb_interface *intf, if (gspca_dev->cam.ctrls != NULL) ctrls_init(gspca_dev); ret = sd_desc->init(gspca_dev); + if (ret < 0) + goto out; + if (sd_desc->init_controls) + ret = sd_desc->init_controls(gspca_dev); if (ret < 0) goto out; gspca_set_default_mode(gspca_dev); @@ -2363,15 +2280,16 @@ int gspca_dev_probe2(struct usb_interface *intf, if (ret) goto out; - mutex_init(&gspca_dev->usb_lock); - mutex_init(&gspca_dev->queue_lock); - init_waitqueue_head(&gspca_dev->wq); + /* + * Don't take usb_lock for these ioctls. This improves latency if + * usb_lock is taken for a long time, e.g. when changing a control + * value, and a new frame is ready to be dequeued. + */ + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); + v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); /* init video stuff */ - memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); - gspca_dev->vdev.parent = &intf->dev; - gspca_dev->module = module; - gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, -1); @@ -2391,6 +2309,7 @@ out: if (gspca_dev->input_dev) input_unregister_device(gspca_dev->input_dev); #endif + v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; @@ -2437,11 +2356,12 @@ void gspca_disconnect(struct usb_interface *intf) PDEBUG(D_PROBE, "%s disconnect", video_device_node_name(&gspca_dev->vdev)); + mutex_lock(&gspca_dev->usb_lock); + usb_set_intfdata(intf, NULL); + gspca_dev->dev = NULL; gspca_dev->present = 0; - wake_up_interruptible(&gspca_dev->wq); - destroy_urbs(gspca_dev); #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) @@ -2452,18 +2372,19 @@ void gspca_disconnect(struct usb_interface *intf) input_unregister_device(input_dev); } #endif + /* Free subdriver's streaming resources / stop sd workqueue(s) */ + if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) + gspca_dev->sd_desc->stop0(gspca_dev); + gspca_dev->streaming = 0; + wake_up_interruptible(&gspca_dev->wq); - /* the device is freed at exit of this function */ - gspca_dev->dev = NULL; - mutex_unlock(&gspca_dev->usb_lock); - - usb_set_intfdata(intf, NULL); - - /* release the device */ - /* (this will call gspca_release() immediately or on last close) */ + v4l2_device_disconnect(&gspca_dev->v4l2_dev); video_unregister_device(&gspca_dev->vdev); -/* PDEBUG(D_PROBE, "disconnect complete"); */ + mutex_unlock(&gspca_dev->usb_lock); + + /* (this will call gspca_release() immediately or on last close) */ + v4l2_device_put(&gspca_dev->v4l2_dev); } EXPORT_SYMBOL(gspca_disconnect); @@ -2474,7 +2395,9 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) if (!gspca_dev->streaming) return 0; + mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 1; /* avoid urb error messages */ + gspca_dev->usb_err = 0; if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); @@ -2482,6 +2405,7 @@ int gspca_suspend(struct usb_interface *intf, pm_message_t message) gspca_set_alt0(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); return 0; } EXPORT_SYMBOL(gspca_suspend); @@ -2489,105 +2413,28 @@ EXPORT_SYMBOL(gspca_suspend); int gspca_resume(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); + int streaming, ret = 0; + mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 0; + gspca_dev->usb_err = 0; gspca_dev->sd_desc->init(gspca_dev); gspca_input_create_urb(gspca_dev); - if (gspca_dev->streaming) - return gspca_init_transfer(gspca_dev); - return 0; + /* + * Most subdrivers send all ctrl values on sd_start and thus + * only write to the device registers on s_ctrl when streaming -> + * Clear streaming to avoid setting all ctrls twice. + */ + streaming = gspca_dev->streaming; + gspca_dev->streaming = 0; + v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); + if (streaming) + ret = gspca_init_transfer(gspca_dev); + mutex_unlock(&gspca_dev->usb_lock); + return ret; } EXPORT_SYMBOL(gspca_resume); #endif -/* -- cam driver utility functions -- */ - -/* auto gain and exposure algorithm based on the knee algorithm described here: - http://ytse.tricolour.net/docs/LowLightOptimization.html - - Returns 0 if no changes were made, 1 if the gain and or exposure settings - where changed. */ -int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, - int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee) -{ - int i, steps, gain, orig_gain, exposure, orig_exposure, autogain; - const struct ctrl *gain_ctrl = NULL; - const struct ctrl *exposure_ctrl = NULL; - const struct ctrl *autogain_ctrl = NULL; - int retval = 0; - - for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { - if (gspca_dev->ctrl_dis & (1 << i)) - continue; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN) - gain_ctrl = &gspca_dev->sd_desc->ctrls[i]; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE) - exposure_ctrl = &gspca_dev->sd_desc->ctrls[i]; - if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN) - autogain_ctrl = &gspca_dev->sd_desc->ctrls[i]; - } - if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) { - PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called " - "on cam without (auto)gain/exposure"); - return 0; - } - - if (gain_ctrl->get(gspca_dev, &gain) || - exposure_ctrl->get(gspca_dev, &exposure) || - autogain_ctrl->get(gspca_dev, &autogain) || !autogain) - return 0; - - orig_gain = gain; - orig_exposure = exposure; - - /* If we are of a multiple of deadzone, do multiple steps to reach the - desired lumination fast (with the risc of a slight overshoot) */ - steps = abs(desired_avg_lum - avg_lum) / deadzone; - - PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d", - avg_lum, desired_avg_lum, steps); - - for (i = 0; i < steps; i++) { - if (avg_lum > desired_avg_lum) { - if (gain > gain_knee) - gain--; - else if (exposure > exposure_knee) - exposure--; - else if (gain > gain_ctrl->qctrl.default_value) - gain--; - else if (exposure > exposure_ctrl->qctrl.minimum) - exposure--; - else if (gain > gain_ctrl->qctrl.minimum) - gain--; - else - break; - } else { - if (gain < gain_ctrl->qctrl.default_value) - gain++; - else if (exposure < exposure_knee) - exposure++; - else if (gain < gain_knee) - gain++; - else if (exposure < exposure_ctrl->qctrl.maximum) - exposure++; - else if (gain < gain_ctrl->qctrl.maximum) - gain++; - else - break; - } - } - - if (gain != orig_gain) { - gain_ctrl->set(gspca_dev, gain); - retval = 1; - } - if (exposure != orig_exposure) { - exposure_ctrl->set(gspca_dev, exposure); - retval = 1; - } - - return retval; -} -EXPORT_SYMBOL(gspca_auto_gain_n_exposure); /* -- module insert / remove -- */ static int __init gspca_init(void) diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h index 589009f4496f..dc688c7f5e48 100644 --- a/drivers/media/video/gspca/gspca.h +++ b/drivers/media/video/gspca/gspca.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include /* compilation option */ @@ -115,6 +117,7 @@ struct sd_desc { /* mandatory operations */ cam_cf_op config; /* called on probe */ cam_op init; /* called on probe and resume */ + cam_op init_controls; /* called on probe */ cam_op start; /* called on stream on after URBs creation */ cam_pkt_op pkt_scan; /* optional operations */ @@ -158,8 +161,10 @@ struct gspca_frame { struct gspca_dev { struct video_device vdev; /* !! must be the first item */ struct module *module; /* subdriver handling the device */ + struct v4l2_device v4l2_dev; struct usb_device *dev; struct file *capt_file; /* file doing video capture */ + /* protected by queue_lock */ #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) struct input_dev *input_dev; char phys[64]; /* physical device path */ @@ -169,6 +174,16 @@ struct gspca_dev { const struct sd_desc *sd_desc; /* subdriver description */ unsigned ctrl_dis; /* disabled controls (bit map) */ unsigned ctrl_inac; /* inactive controls (bit map) */ + struct v4l2_ctrl_handler ctrl_handler; + + /* autogain and exposure or gain control cluster, these are global as + the autogain/exposure functions in autogain_functions.c use them */ + struct { + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + int exp_too_low_cnt, exp_too_high_cnt; + }; #define USB_BUF_SZ 64 __u8 *usb_buf; /* buffer for USB exchanges */ @@ -189,7 +204,7 @@ struct gspca_dev { u8 fr_o; /* next frame to dequeue */ __u8 last_packet_type; __s8 empty_packet; /* if (-1) don't check empty packets */ - __u8 streaming; + __u8 streaming; /* protected by both mutexes (*) */ __u8 curr_mode; /* current camera mode */ __u32 pixfmt; /* current mode parameters */ @@ -211,6 +226,10 @@ struct gspca_dev { __u8 iface; /* USB interface number */ __u8 alt; /* USB alternate setting */ u8 audio; /* presence of audio device */ + + /* (*) These variables are proteced by both usb_lock and queue_lock, + that is any code setting them is holding *both*, which means that + any code getting them needs to hold at least one of them */ }; int gspca_dev_probe(struct usb_interface *intf, @@ -232,6 +251,9 @@ void gspca_frame_add(struct gspca_dev *gspca_dev, int gspca_suspend(struct usb_interface *intf, pm_message_t message); int gspca_resume(struct usb_interface *intf); #endif -int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, +int gspca_expo_autogain(struct gspca_dev *gspca_dev, int avg_lum, int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee); +int gspca_coarse_grained_expo_autogain(struct gspca_dev *gspca_dev, + int avg_lum, int desired_avg_lum, int deadzone); + #endif /* GSPCAV2_H */ diff --git a/drivers/media/video/gspca/jl2005bcd.c b/drivers/media/video/gspca/jl2005bcd.c index 53f58ef367cf..9c591c7c6f54 100644 --- a/drivers/media/video/gspca/jl2005bcd.c +++ b/drivers/media/video/gspca/jl2005bcd.c @@ -335,7 +335,11 @@ static void jl2005c_dostream(struct work_struct *work) goto quit_stream; } - while (gspca_dev->present && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif /* Check if this is a new frame. If so, start the frame first */ if (!header_read) { mutex_lock(&gspca_dev->usb_lock); @@ -367,7 +371,7 @@ static void jl2005c_dostream(struct work_struct *work) buffer, act_len); header_read = 1; } - while (bytes_left > 0 && gspca_dev->present) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > JL2005C_MAX_TRANSFER ? JL2005C_MAX_TRANSFER : bytes_left; ret = usb_bulk_msg(gspca_dev->dev, @@ -390,7 +394,7 @@ static void jl2005c_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); jl2005c_stop(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c index b0231465afae..ec7b21ee79fb 100644 --- a/drivers/media/video/gspca/mars.c +++ b/drivers/media/video/gspca/mars.c @@ -30,22 +30,19 @@ MODULE_AUTHOR("Michel Xhaard "); MODULE_DESCRIPTION("GSPCA/Mars USB Camera Driver"); MODULE_LICENSE("GPL"); -/* controls */ -enum e_ctrl { - BRIGHTNESS, - COLORS, - GAMMA, - SHARPNESS, - ILLUM_TOP, - ILLUM_BOT, - NCTRLS /* number of controls */ -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct gspca_ctrl ctrls[NCTRLS]; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *gamma; + struct { /* illuminator control cluster */ + struct v4l2_ctrl *illum_top; + struct v4l2_ctrl *illum_bottom; + }; + struct v4l2_ctrl *jpegqual; u8 quality; #define QUALITY_MIN 40 @@ -56,89 +53,10 @@ struct sd { }; /* V4L2 controls supported by the driver */ -static void setbrightness(struct gspca_dev *gspca_dev); -static void setcolors(struct gspca_dev *gspca_dev); -static void setgamma(struct gspca_dev *gspca_dev); -static void setsharpness(struct gspca_dev *gspca_dev); -static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val); -static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 30, - .step = 1, - .default_value = 15, - }, - .set_control = setbrightness - }, -[COLORS] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Color", - .minimum = 1, - .maximum = 255, - .step = 1, - .default_value = 200, - }, - .set_control = setcolors - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 1, - }, - .set_control = setgamma - }, -[SHARPNESS] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 2, - .step = 1, - .default_value = 1, - }, - .set_control = setsharpness - }, -[ILLUM_TOP] = { - { - .id = V4L2_CID_ILLUMINATORS_1, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Top illuminator", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_UPDATE, - }, - .set = sd_setilluminator1 - }, -[ILLUM_BOT] = { - { - .id = V4L2_CID_ILLUMINATORS_2, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Bottom illuminator", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - .flags = V4L2_CTRL_FLAG_UPDATE, - }, - .set = sd_setilluminator2 - }, -}; +static void setbrightness(struct gspca_dev *gspca_dev, s32 val); +static void setcolors(struct gspca_dev *gspca_dev, s32 val); +static void setgamma(struct gspca_dev *gspca_dev, s32 val); +static void setsharpness(struct gspca_dev *gspca_dev, s32 val); static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, @@ -198,59 +116,130 @@ static void mi_w(struct gspca_dev *gspca_dev, reg_w(gspca_dev, 4); } -static void setbrightness(struct gspca_dev *gspca_dev) +static void setbrightness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x61; - gspca_dev->usb_buf[1] = sd->ctrls[BRIGHTNESS].val; + gspca_dev->usb_buf[1] = val; reg_w(gspca_dev, 2); } -static void setcolors(struct gspca_dev *gspca_dev) +static void setcolors(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - s16 val; - - val = sd->ctrls[COLORS].val; gspca_dev->usb_buf[0] = 0x5f; gspca_dev->usb_buf[1] = val << 3; gspca_dev->usb_buf[2] = ((val >> 2) & 0xf8) | 0x04; reg_w(gspca_dev, 3); } -static void setgamma(struct gspca_dev *gspca_dev) +static void setgamma(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x06; - gspca_dev->usb_buf[1] = sd->ctrls[GAMMA].val * 0x40; + gspca_dev->usb_buf[1] = val * 0x40; reg_w(gspca_dev, 2); } -static void setsharpness(struct gspca_dev *gspca_dev) +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->usb_buf[0] = 0x67; - gspca_dev->usb_buf[1] = sd->ctrls[SHARPNESS].val * 4 + 3; + gspca_dev->usb_buf[1] = val * 4 + 3; reg_w(gspca_dev, 2); } -static void setilluminators(struct gspca_dev *gspca_dev) +static void setilluminators(struct gspca_dev *gspca_dev, bool top, bool bottom) { - struct sd *sd = (struct sd *) gspca_dev; - + /* both are off if not streaming */ gspca_dev->usb_buf[0] = 0x22; - if (sd->ctrls[ILLUM_TOP].val) + if (top) gspca_dev->usb_buf[1] = 0x76; - else if (sd->ctrls[ILLUM_BOT].val) + else if (bottom) gspca_dev->usb_buf[1] = 0x7a; else gspca_dev->usb_buf[1] = 0x7e; reg_w(gspca_dev, 2); } +static int mars_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_ILLUMINATORS_1) { + /* only one can be on at a time */ + if (ctrl->is_new && ctrl->val) + sd->illum_bottom->val = 0; + if (sd->illum_bottom->is_new && sd->illum_bottom->val) + sd->illum_top->val = 0; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setbrightness(gspca_dev, ctrl->val); + break; + case V4L2_CID_SATURATION: + setcolors(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAMMA: + setgamma(gspca_dev, ctrl->val); + break; + case V4L2_CID_ILLUMINATORS_1: + setilluminators(gspca_dev, sd->illum_top->val, + sd->illum_bottom->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + jpeg_set_qual(sd->jpeg_hdr, ctrl->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops mars_ctrl_ops = { + .s_ctrl = mars_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 7); + sd->brightness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 30, 1, 15); + sd->saturation = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 200); + sd->gamma = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_GAMMA, 0, 3, 1, 1); + sd->sharpness = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 2, 1, 1); + sd->illum_top = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_ILLUMINATORS_1, 0, 1, 1, 0); + sd->illum_top->flags |= V4L2_CTRL_FLAG_UPDATE; + sd->illum_bottom = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_ILLUMINATORS_2, 0, 1, 1, 0); + sd->illum_bottom->flags |= V4L2_CTRL_FLAG_UPDATE; + sd->jpegqual = v4l2_ctrl_new_std(hdl, &mars_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(2, &sd->illum_top); + return 0; +} + /* this function is called at probe time */ static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) @@ -261,7 +250,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); - cam->ctrls = sd->ctrls; sd->quality = QUALITY_DEF; return 0; } @@ -269,7 +257,6 @@ static int sd_config(struct gspca_dev *gspca_dev, /* this function is called at probe and resume time */ static int sd_init(struct gspca_dev *gspca_dev) { - gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT); return 0; } @@ -282,7 +269,7 @@ static int sd_start(struct gspca_dev *gspca_dev) /* create the JPEG header */ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width, 0x21); /* JPEG 422 */ - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); data = gspca_dev->usb_buf; @@ -301,7 +288,7 @@ static int sd_start(struct gspca_dev *gspca_dev) data[5] = 0x30; /* reg 4, MI, PAS5101 : * 0x30 for 24mhz , 0x28 for 12mhz */ data[6] = 0x02; /* reg 5, H start - was 0x04 */ - data[7] = sd->ctrls[GAMMA].val * 0x40; /* reg 0x06: gamma */ + data[7] = v4l2_ctrl_g_ctrl(sd->gamma) * 0x40; /* reg 0x06: gamma */ data[8] = 0x01; /* reg 7, V start - was 0x03 */ /* if (h_size == 320 ) */ /* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */ @@ -333,16 +320,16 @@ static int sd_start(struct gspca_dev *gspca_dev) /* reg 0x5f/0x60 (LE) = saturation */ /* h (60): xxxx x100 * l (5f): xxxx x000 */ - data[2] = sd->ctrls[COLORS].val << 3; - data[3] = ((sd->ctrls[COLORS].val >> 2) & 0xf8) | 0x04; - data[4] = sd->ctrls[BRIGHTNESS].val; /* reg 0x61 = brightness */ + data[2] = v4l2_ctrl_g_ctrl(sd->saturation) << 3; + data[3] = ((v4l2_ctrl_g_ctrl(sd->saturation) >> 2) & 0xf8) | 0x04; + data[4] = v4l2_ctrl_g_ctrl(sd->brightness); /* reg 0x61 = brightness */ data[5] = 0x00; reg_w(gspca_dev, 6); data[0] = 0x67; /*jfm: from win trace*/ - data[1] = sd->ctrls[SHARPNESS].val * 4 + 3; + data[1] = v4l2_ctrl_g_ctrl(sd->sharpness) * 4 + 3; data[2] = 0x14; reg_w(gspca_dev, 3); @@ -365,7 +352,9 @@ static int sd_start(struct gspca_dev *gspca_dev) data[1] = 0x4d; /* ISOC transferring enable... */ reg_w(gspca_dev, 2); - gspca_dev->ctrl_inac = 0; /* activate the illuminator controls */ + setilluminators(gspca_dev, v4l2_ctrl_g_ctrl(sd->illum_top), + v4l2_ctrl_g_ctrl(sd->illum_bottom)); + return gspca_dev->usb_err; } @@ -373,11 +362,9 @@ static void sd_stopN(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - gspca_dev->ctrl_inac = (1 << ILLUM_TOP) | (1 << ILLUM_BOT); - if (sd->ctrls[ILLUM_TOP].val || sd->ctrls[ILLUM_BOT].val) { - sd->ctrls[ILLUM_TOP].val = 0; - sd->ctrls[ILLUM_BOT].val = 0; - setilluminators(gspca_dev); + if (v4l2_ctrl_g_ctrl(sd->illum_top) || + v4l2_ctrl_g_ctrl(sd->illum_bottom)) { + setilluminators(gspca_dev, false, false); msleep(20); } @@ -424,43 +411,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setilluminator1(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* only one illuminator may be on */ - sd->ctrls[ILLUM_TOP].val = val; - if (val) - sd->ctrls[ILLUM_BOT].val = 0; - setilluminators(gspca_dev); - return gspca_dev->usb_err; -} - -static int sd_setilluminator2(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - /* only one illuminator may be on */ - sd->ctrls[ILLUM_BOT].val = val; - if (val) - sd->ctrls[ILLUM_TOP].val = 0; - setilluminators(gspca_dev); - return gspca_dev->usb_err; -} - static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - if (jcomp->quality < QUALITY_MIN) - sd->quality = QUALITY_MIN; - else if (jcomp->quality > QUALITY_MAX) - sd->quality = QUALITY_MAX; - else - sd->quality = jcomp->quality; - if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->quality); + ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + if (ret) + return ret; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); return 0; } @@ -470,7 +430,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->quality; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -479,10 +439,9 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = NCTRLS, .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, @@ -513,6 +472,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; diff --git a/drivers/media/video/gspca/nw80x.c b/drivers/media/video/gspca/nw80x.c index 7167cac7359c..42e021931e60 100644 --- a/drivers/media/video/gspca/nw80x.c +++ b/drivers/media/video/gspca/nw80x.c @@ -2001,6 +2001,8 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) return gspca_dev->usb_err; } +#define WANT_REGULAR_AUTOGAIN +#define WANT_COARSE_EXPO_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c index 739e8a2a2d30..183457c5cfdb 100644 --- a/drivers/media/video/gspca/ov519.c +++ b/drivers/media/video/gspca/ov519.c @@ -2804,7 +2804,7 @@ static void ov7xx0_configure(struct sd *sd) /* add OV7670 here * it appears to be wrongly detected as a 7610 by default */ if (rc < 0) { - PDEBUG(D_ERR, "Error detecting sensor type"); + pr_err("Error detecting sensor type\n"); return; } if ((rc & 3) == 3) { @@ -2832,12 +2832,12 @@ static void ov7xx0_configure(struct sd *sd) /* try to read product id registers */ high = i2c_r(sd, 0x0a); if (high < 0) { - PDEBUG(D_ERR, "Error detecting camera chip PID"); + pr_err("Error detecting camera chip PID\n"); return; } low = i2c_r(sd, 0x0b); if (low < 0) { - PDEBUG(D_ERR, "Error detecting camera chip VER"); + pr_err("Error detecting camera chip VER\n"); return; } if (high == 0x76) { @@ -2863,7 +2863,7 @@ static void ov7xx0_configure(struct sd *sd) sd->sensor = SEN_OV7660; break; default: - PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low); + pr_err("Unknown sensor: 0x76%02x\n", low); return; } } else { @@ -2884,7 +2884,7 @@ static void ov6xx0_configure(struct sd *sd) /* Detect sensor (sub)type */ rc = i2c_r(sd, OV7610_REG_COM_I); if (rc < 0) { - PDEBUG(D_ERR, "Error detecting sensor type"); + pr_err("Error detecting sensor type\n"); return; } diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c index 04753391de3e..b5acb1e4b4e7 100644 --- a/drivers/media/video/gspca/ov534.c +++ b/drivers/media/video/gspca/ov534.c @@ -34,6 +34,8 @@ #include "gspca.h" +#include + #define OV534_REG_ADDRESS 0xf1 /* sensor address */ #define OV534_REG_SUBADDR 0xf2 #define OV534_REG_WRITE 0xf3 @@ -53,6 +55,8 @@ MODULE_LICENSE("GPL"); /* controls */ enum e_ctrl { + HUE, + SATURATION, BRIGHTNESS, CONTRAST, GAIN, @@ -63,7 +67,6 @@ enum e_ctrl { SHARPNESS, HFLIP, VFLIP, - COLORS, LIGHTFREQ, NCTRLS /* number of controls */ }; @@ -87,6 +90,8 @@ enum sensors { }; /* V4L2 controls supported by the driver */ +static void sethue(struct gspca_dev *gspca_dev); +static void setsaturation(struct gspca_dev *gspca_dev); static void setbrightness(struct gspca_dev *gspca_dev); static void setcontrast(struct gspca_dev *gspca_dev); static void setgain(struct gspca_dev *gspca_dev); @@ -96,13 +101,36 @@ static void setawb(struct gspca_dev *gspca_dev); static void setaec(struct gspca_dev *gspca_dev); static void setsharpness(struct gspca_dev *gspca_dev); static void sethvflip(struct gspca_dev *gspca_dev); -static void setcolors(struct gspca_dev *gspca_dev); static void setlightfreq(struct gspca_dev *gspca_dev); static int sd_start(struct gspca_dev *gspca_dev); static void sd_stopN(struct gspca_dev *gspca_dev); static const struct ctrl sd_ctrls[] = { +[HUE] = { + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -90, + .maximum = 90, + .step = 1, + .default_value = 0, + }, + .set_control = sethue + }, +[SATURATION] = { + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 64, + }, + .set_control = setsaturation + }, [BRIGHTNESS] = { { .id = V4L2_CID_BRIGHTNESS, @@ -223,18 +251,6 @@ static const struct ctrl sd_ctrls[] = { }, .set_control = sethvflip }, -[COLORS] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 6, - .step = 1, - .default_value = 3, - }, - .set_control = setcolors - }, [LIGHTFREQ] = { { .id = V4L2_CID_POWER_LINE_FREQUENCY, @@ -684,7 +700,7 @@ static const u8 sensor_init_772x[][2] = { { 0x9c, 0x20 }, { 0x9e, 0x81 }, - { 0xa6, 0x04 }, + { 0xa6, 0x07 }, { 0x7e, 0x0c }, { 0x7f, 0x16 }, { 0x80, 0x2a }, @@ -955,6 +971,74 @@ static void set_frame_rate(struct gspca_dev *gspca_dev) PDEBUG(D_PROBE, "frame_rate: %d", r->fps); } +static void sethue(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; + + val = sd->ctrls[HUE].val; + if (sd->sensor == SENSOR_OV767x) { + /* TBD */ + } else { + s16 huesin; + s16 huecos; + + /* fixp_sin and fixp_cos accept only positive values, while + * our val is between -90 and 90 + */ + val += 360; + + /* According to the datasheet the registers expect HUESIN and + * HUECOS to be the result of the trigonometric functions, + * scaled by 0x80. + * + * The 0x100 here represents the maximun absolute value + * returned byt fixp_sin and fixp_cos, so the scaling will + * consider the result like in the interval [-1.0, 1.0]. + */ + huesin = fixp_sin(val) * 0x80 / 0x100; + huecos = fixp_cos(val) * 0x80 / 0x100; + + if (huesin < 0) { + sccb_reg_write(gspca_dev, 0xab, + sccb_reg_read(gspca_dev, 0xab) | 0x2); + huesin = -huesin; + } else { + sccb_reg_write(gspca_dev, 0xab, + sccb_reg_read(gspca_dev, 0xab) & ~0x2); + + } + sccb_reg_write(gspca_dev, 0xa9, (u8)huecos); + sccb_reg_write(gspca_dev, 0xaa, (u8)huesin); + } +} + +static void setsaturation(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + int val; + + val = sd->ctrls[SATURATION].val; + if (sd->sensor == SENSOR_OV767x) { + int i; + static u8 color_tb[][6] = { + {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, + {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, + {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, + {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, + {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, + {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, + {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, + }; + + for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) + sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); + } else { + sccb_reg_write(gspca_dev, 0xa7, val); /* U saturation */ + sccb_reg_write(gspca_dev, 0xa8, val); /* V saturation */ + } +} + static void setbrightness(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1132,26 +1216,6 @@ static void sethvflip(struct gspca_dev *gspca_dev) } } -static void setcolors(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - u8 val; - int i; - static u8 color_tb[][6] = { - {0x42, 0x42, 0x00, 0x11, 0x30, 0x41}, - {0x52, 0x52, 0x00, 0x16, 0x3c, 0x52}, - {0x66, 0x66, 0x00, 0x1b, 0x4b, 0x66}, - {0x80, 0x80, 0x00, 0x22, 0x5e, 0x80}, - {0x9a, 0x9a, 0x00, 0x29, 0x71, 0x9a}, - {0xb8, 0xb8, 0x00, 0x31, 0x87, 0xb8}, - {0xdd, 0xdd, 0x00, 0x3b, 0xa2, 0xdd}, - }; - - val = sd->ctrls[COLORS].val; - for (i = 0; i < ARRAY_SIZE(color_tb[0]); i++) - sccb_reg_write(gspca_dev, 0x4f + i, color_tb[val][i]); -} - static void setlightfreq(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1225,9 +1289,13 @@ static int sd_init(struct gspca_dev *gspca_dev) if ((sensor_id & 0xfff0) == 0x7670) { sd->sensor = SENSOR_OV767x; - gspca_dev->ctrl_dis = (1 << GAIN) | + gspca_dev->ctrl_dis = (1 << HUE) | + (1 << GAIN) | (1 << AGC) | (1 << SHARPNESS); /* auto */ + sd->ctrls[SATURATION].min = 0, + sd->ctrls[SATURATION].max = 6, + sd->ctrls[SATURATION].def = 3, sd->ctrls[BRIGHTNESS].min = -127; sd->ctrls[BRIGHTNESS].max = 127; sd->ctrls[BRIGHTNESS].def = 0; @@ -1243,7 +1311,6 @@ static int sd_init(struct gspca_dev *gspca_dev) gspca_dev->cam.nmodes = ARRAY_SIZE(ov767x_mode); } else { sd->sensor = SENSOR_OV772x; - gspca_dev->ctrl_dis = (1 << COLORS); gspca_dev->cam.bulk = 1; gspca_dev->cam.bulk_size = 16384; gspca_dev->cam.bulk_nurbs = 2; @@ -1302,6 +1369,9 @@ static int sd_start(struct gspca_dev *gspca_dev) set_frame_rate(gspca_dev); + if (!(gspca_dev->ctrl_dis & (1 << HUE))) + sethue(gspca_dev); + setsaturation(gspca_dev); if (!(gspca_dev->ctrl_dis & (1 << AGC))) setagc(gspca_dev); setawb(gspca_dev); @@ -1314,8 +1384,6 @@ static int sd_start(struct gspca_dev *gspca_dev) if (!(gspca_dev->ctrl_dis & (1 << SHARPNESS))) setsharpness(gspca_dev); sethvflip(gspca_dev); - if (!(gspca_dev->ctrl_dis & (1 << COLORS))) - setcolors(gspca_dev); setlightfreq(gspca_dev); ov534_set_led(gspca_dev, 1); diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c index 3844c49f269c..fa661c6d6d55 100644 --- a/drivers/media/video/gspca/pac207.c +++ b/drivers/media/video/gspca/pac207.c @@ -29,6 +29,8 @@ #include #include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("Pixart PAC207"); @@ -39,16 +41,17 @@ MODULE_LICENSE("GPL"); #define PAC207_BRIGHTNESS_MIN 0 #define PAC207_BRIGHTNESS_MAX 255 #define PAC207_BRIGHTNESS_DEFAULT 46 +#define PAC207_BRIGHTNESS_REG 0x08 #define PAC207_EXPOSURE_MIN 3 #define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */ #define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */ -#define PAC207_EXPOSURE_KNEE 9 /* fps: 90 / exposure -> 9: 10 fps */ +#define PAC207_EXPOSURE_REG 0x02 #define PAC207_GAIN_MIN 0 #define PAC207_GAIN_MAX 31 #define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */ -#define PAC207_GAIN_KNEE 15 +#define PAC207_GAIN_REG 0x0e #define PAC207_AUTOGAIN_DEADZONE 30 @@ -56,13 +59,9 @@ MODULE_LICENSE("GPL"); struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ + struct v4l2_ctrl *brightness; + u8 mode; - - u8 brightness; - u8 exposure; - u8 autogain; - u8 gain; - u8 sof_read; u8 header_read; u8 autogain_ignore_frames; @@ -70,80 +69,6 @@ struct sd { 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_setexposure(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getexposure(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_setgain(struct gspca_dev *gspca_dev, __s32 val); -static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val); - -static const struct ctrl sd_ctrls[] = { -#define SD_BRIGHTNESS 0 - { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = PAC207_BRIGHTNESS_MIN, - .maximum = PAC207_BRIGHTNESS_MAX, - .step = 1, - .default_value = PAC207_BRIGHTNESS_DEFAULT, - .flags = 0, - }, - .set = sd_setbrightness, - .get = sd_getbrightness, - }, -#define SD_EXPOSURE 1 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = PAC207_EXPOSURE_MIN, - .maximum = PAC207_EXPOSURE_MAX, - .step = 1, - .default_value = PAC207_EXPOSURE_DEFAULT, - .flags = 0, - }, - .set = sd_setexposure, - .get = sd_getexposure, - }, -#define SD_AUTOGAIN 2 - { - { - .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, - .flags = 0, - }, - .set = sd_setautogain, - .get = sd_getautogain, - }, -#define SD_GAIN 3 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = PAC207_GAIN_MIN, - .maximum = PAC207_GAIN_MAX, - .step = 1, - .default_value = PAC207_GAIN_DEFAULT, - .flags = 0, - }, - .set = sd_setgain, - .get = sd_getgain, - }, -}; - static const struct v4l2_pix_format sif_mode[] = { {176, 144, V4L2_PIX_FMT_PAC207, V4L2_FIELD_NONE, .bytesperline = 176, @@ -167,39 +92,44 @@ static const __u8 pac207_sensor_init[][8] = { {0x32, 0x00, 0x96, 0x00, 0xa2, 0x02, 0xaf, 0x00}, }; -static int pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, +static void pac207_write_regs(struct gspca_dev *gspca_dev, u16 index, const u8 *buffer, u16 length) { struct usb_device *udev = gspca_dev->dev; int err; + if (gspca_dev->usb_err < 0) + return; + memcpy(gspca_dev->usb_buf, buffer, length); err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x01, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, index, gspca_dev->usb_buf, length, PAC207_CTRL_TIMEOUT); - if (err < 0) + if (err < 0) { pr_err("Failed to write registers to index 0x%04X, error %d\n", index, err); - - return err; + gspca_dev->usb_err = err; + } } - -static int pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) +static void pac207_write_reg(struct gspca_dev *gspca_dev, u16 index, u16 value) { struct usb_device *udev = gspca_dev->dev; int err; + if (gspca_dev->usb_err < 0) + return; + err = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0x00, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, value, index, NULL, 0, PAC207_CTRL_TIMEOUT); - if (err) + if (err) { pr_err("Failed to write a register (index 0x%04X, value 0x%02X, error %d)\n", index, value, err); - - return err; + gspca_dev->usb_err = err; + } } static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) @@ -207,6 +137,9 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) struct usb_device *udev = gspca_dev->dev; int res; + if (gspca_dev->usb_err < 0) + return 0; + res = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), 0x00, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, 0x00, index, @@ -214,7 +147,8 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) if (res < 0) { pr_err("Failed to read a register (index 0x%04X, error %d)\n", index, res); - return res; + gspca_dev->usb_err = res; + return 0; } return gspca_dev->usb_buf[0]; @@ -224,7 +158,6 @@ static int pac207_read_reg(struct gspca_dev *gspca_dev, u16 index) 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 idreg[2]; @@ -247,10 +180,6 @@ static int sd_config(struct gspca_dev *gspca_dev, cam = &gspca_dev->cam; cam->cam_mode = sif_mode; cam->nmodes = ARRAY_SIZE(sif_mode); - sd->brightness = PAC207_BRIGHTNESS_DEFAULT; - sd->exposure = PAC207_EXPOSURE_DEFAULT; - sd->gain = PAC207_GAIN_DEFAULT; - sd->autogain = AUTOGAIN_DEF; return 0; } @@ -264,6 +193,87 @@ static int sd_init(struct gspca_dev *gspca_dev) * Bit_2=Compression test mode enable */ pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ + return gspca_dev->usb_err; +} + +static void setcontrol(struct gspca_dev *gspca_dev, u16 reg, u16 val) +{ + pac207_write_reg(gspca_dev, reg, val); + pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ + pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ +} + +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->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. */ + gspca_dev->exposure->val = PAC207_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC207_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + setcontrol(gspca_dev, PAC207_BRIGHTNESS_REG, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setcontrol(gspca_dev, PAC207_EXPOSURE_REG, + gspca_dev->exposure->val); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setcontrol(gspca_dev, PAC207_GAIN_REG, + gspca_dev->gain->val); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, + PAC207_BRIGHTNESS_MIN, PAC207_BRIGHTNESS_MAX, + 1, PAC207_BRIGHTNESS_DEFAULT); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, + PAC207_EXPOSURE_MIN, PAC207_EXPOSURE_MAX, + 1, PAC207_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, + PAC207_GAIN_MIN, PAC207_GAIN_MAX, + 1, PAC207_GAIN_DEFAULT); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); return 0; } @@ -285,11 +295,13 @@ static int sd_start(struct gspca_dev *gspca_dev) else pac207_write_reg(gspca_dev, 0x4a, 0x30); pac207_write_reg(gspca_dev, 0x4b, 0x00); /* Sram test value */ - pac207_write_reg(gspca_dev, 0x08, sd->brightness); + pac207_write_reg(gspca_dev, 0x08, v4l2_ctrl_g_ctrl(sd->brightness)); /* PGA global gain (Bit 4-0) */ - pac207_write_reg(gspca_dev, 0x0e, sd->gain); - pac207_write_reg(gspca_dev, 0x02, sd->exposure); /* PXCK = 12MHz /n */ + pac207_write_reg(gspca_dev, 0x0e, + v4l2_ctrl_g_ctrl(gspca_dev->gain)); + pac207_write_reg(gspca_dev, 0x02, + v4l2_ctrl_g_ctrl(gspca_dev->exposure)); /* PXCK = 12MHz /n */ mode = 0x02; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */ if (gspca_dev->width == 176) { /* 176x144 */ @@ -308,7 +320,7 @@ static int sd_start(struct gspca_dev *gspca_dev) sd->sof_read = 0; sd->autogain_ignore_frames = 0; atomic_set(&sd->avg_lum, -1); - return 0; + return gspca_dev->usb_err; } static void sd_stopN(struct gspca_dev *gspca_dev) @@ -318,8 +330,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) pac207_write_reg(gspca_dev, 0x0f, 0x00); /* Power Control */ } -/* Include pac common sof detection functions */ -#include "pac_common.h" static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) { @@ -331,9 +341,8 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev) if (sd->autogain_ignore_frames > 0) sd->autogain_ignore_frames--; - else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, - 90, PAC207_AUTOGAIN_DEADZONE, - PAC207_GAIN_KNEE, PAC207_EXPOSURE_KNEE)) + else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + 90, PAC207_AUTOGAIN_DEADZONE)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } @@ -384,118 +393,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static void setbrightness(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x08, sd->brightness); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static void setexposure(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x02, sd->exposure); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -static void setgain(struct gspca_dev *gspca_dev) -{ - struct sd *sd = (struct sd *) gspca_dev; - - pac207_write_reg(gspca_dev, 0x0e, sd->gain); - pac207_write_reg(gspca_dev, 0x13, 0x01); /* Bit 0, auto clear */ - pac207_write_reg(gspca_dev, 0x1c, 0x01); /* not documented */ -} - -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) - setbrightness(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_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_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_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 = PAC207_EXPOSURE_DEFAULT; - sd->gain = PAC207_GAIN_DEFAULT; - 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; -} - #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ @@ -518,10 +415,9 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, /* 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, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .dq_callback = pac207_do_auto_gain, diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c index 30662fccb0cf..a0369a58c4bb 100644 --- a/drivers/media/video/gspca/pac7302.c +++ b/drivers/media/video/gspca/pac7302.c @@ -23,43 +23,58 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -/* Some documentation about various registers as determined by trial and error. - - Register page 1: - - Address Description - 0x78 Global control, bit 6 controls the LED (inverted) - - Register page 3: - - Address Description - 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on - the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? - 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps - 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, - 63 -> ~27 fps, the 2 msb's must always be 1 !! - 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: - 1 -> ~30 fps, 2 -> ~20 fps - 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time - 0x0f Exposure bit 8, 0-448, 448 = no exposure at all - 0x10 Master gain 0-31 - 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused - - 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() -*/ +/* + * Some documentation about various registers as determined by trial and error. + * + * Register page 1: + * + * Address Description + * 0x78 Global control, bit 6 controls the LED (inverted) + * 0x80 Compression balance, 2 interesting settings: + * 0x0f Default + * 0x50 Values >= this switch the camera to a lower compression, + * using the same table for both luminance and chrominance. + * This gives a sharper picture. Only usable when running + * at < 15 fps! Note currently the driver does not use this + * as the quality gain is small and the generated JPG-s are + * only understood by v4l-utils >= 0.8.9 + * + * Register page 3: + * + * Address Description + * 0x02 Clock divider 3-63, fps = 90 / val. Must be a multiple of 3 on + * the 7302, so one of 3, 6, 9, ..., except when between 6 and 12? + * 0x03 Variable framerate ctrl reg2==3: 0 -> ~30 fps, 255 -> ~22fps + * 0x04 Another var framerate ctrl reg2==3, reg3==0: 0 -> ~30 fps, + * 63 -> ~27 fps, the 2 msb's must always be 1 !! + * 0x05 Another var framerate ctrl reg2==3, reg3==0, reg4==0xc0: + * 1 -> ~30 fps, 2 -> ~20 fps + * 0x0e Exposure bits 0-7, 0-448, 0 = use full frame time + * 0x0f Exposure bit 8, 0-448, 448 = no exposure at all + * 0x10 Gain 0-31 + * 0x12 Another gain 0-31, unlike 0x10 this one seems to start with an + * amplification value of 1 rather then 0 at its lowest setting + * 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused + * 0x80 Another framerate control, best left at 1, moving it from 1 to + * 2 causes the framerate to become 3/4th of what it was, and + * also seems to cause pixel averaging, resulting in an effective + * resolution of 320x240 and thus a much blockier image + * + * 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, 0x12 | setgain() + * 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip() + * 3 | 0x21 | sethvflip() + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -89,7 +104,6 @@ enum e_ctrl { NCTRLS /* number of controls */ }; -/* specific webcam descriptor for pac7302 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ @@ -198,10 +212,10 @@ static const struct ctrl sd_ctrls[] = { .type = V4L2_CTRL_TYPE_INTEGER, .name = "Gain", .minimum = 0, - .maximum = 255, + .maximum = 62, .step = 1, -#define GAIN_DEF 127 -#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */ +#define GAIN_DEF 15 +#define GAIN_KNEE 46 .default_value = GAIN_DEF, }, .set_control = setgain @@ -270,7 +284,6 @@ static const struct v4l2_pix_format vga_mode[] = { #define LOAD_PAGE3 255 #define END_OF_SEQUENCE 0 -/* pac 7302 */ static const u8 init_7302[] = { /* index,value */ 0xff, 0x01, /* page 1 */ @@ -509,7 +522,6 @@ 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; @@ -536,7 +548,6 @@ static void setbrightcont(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0xdc, 0x01); } -/* This function is used by pac7302 only */ static void setcolors(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -590,9 +601,19 @@ static void setbluebalance(struct gspca_dev *gspca_dev) static void setgain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; + u8 reg10, reg12; + + if (sd->ctrls[GAIN].val < 32) { + reg10 = sd->ctrls[GAIN].val; + reg12 = 0; + } else { + reg10 = 31; + reg12 = sd->ctrls[GAIN].val - 31; + } reg_w(gspca_dev, 0xff, 0x03); /* page 3 */ - reg_w(gspca_dev, 0x10, sd->ctrls[GAIN].val >> 3); + reg_w(gspca_dev, 0x10, reg10); + reg_w(gspca_dev, 0x12, reg12); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); @@ -604,28 +625,36 @@ static void setexposure(struct gspca_dev *gspca_dev) u8 clockdiv; u16 exposure; - /* register 2 of frame 3 contains the clock divider configuring the - no fps according to the formula: 90 / reg. sd->exposure is the - desired exposure time in 0.5 ms. */ + /* + * Register 2 of frame 3 contains the clock divider configuring the + * no fps according to the formula: 90 / reg. sd->exposure is the + * desired exposure time in 0.5 ms. + */ clockdiv = (90 * sd->ctrls[EXPOSURE].val + 1999) / 2000; - /* Note clockdiv = 3 also works, but when running at 30 fps, depending - on the scene being recorded, the camera switches to another - quantization table for certain JPEG blocks, and we don't know how - to decompress these blocks. So we cap the framerate at 15 fps */ + /* + * Note clockdiv = 3 also works, but when running at 30 fps, depending + * on the scene being recorded, the camera switches to another + * quantization table for certain JPEG blocks, and we don't know how + * to decompress these blocks. So we cap the framerate at 15 fps. + */ if (clockdiv < 6) clockdiv = 6; else if (clockdiv > 63) clockdiv = 63; - /* reg2 MUST be a multiple of 3, except when between 6 and 12? - Always round up, otherwise we cannot get the desired frametime - using the partial frame time exposure control */ + /* + * Register 2 MUST be a multiple of 3, except when between 6 and 12? + * Always round up, otherwise we cannot get the desired frametime + * using the partial frame time exposure control. + */ if (clockdiv < 6 || clockdiv > 12) clockdiv = ((clockdiv + 2) / 3) * 3; - /* frame exposure time in ms = 1000 * clockdiv / 90 -> - exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) */ + /* + * frame exposure time in ms = 1000 * clockdiv / 90 -> + * exposure = (sd->exposure / 2) * 448 / (1000 * clockdiv / 90) + */ exposure = (sd->ctrls[EXPOSURE].val * 45 * 448) / (1000 * clockdiv); /* 0 = use full frametime, 448 = no exposure, reverse it */ exposure = 448 - exposure; @@ -643,10 +672,12 @@ static void setautogain(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - /* 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. */ + /* + * 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->ctrls[AUTOGAIN].val) { sd->ctrls[EXPOSURE].val = EXPOSURE_DEF; sd->ctrls[GAIN].val = GAIN_DEF; @@ -700,8 +731,6 @@ static int sd_start(struct gspca_dev *gspca_dev) setautogain(gspca_dev); sethvflip(gspca_dev); - /* only resolution 640x480 is supported for pac7302 */ - sd->sof_read = 0; atomic_set(&sd->avg_lum, 270 + sd->ctrls[BRIGHTNESS].val); @@ -729,9 +758,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x78, 0x40); } -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt flags -#define exp_too_high_cnt sof_read +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) @@ -792,10 +819,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (sof) { int n, lum_offset, footer_length; - /* 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 */ + /* + * 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; @@ -839,9 +868,10 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, u8 index; u8 value; - /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit - long on the USB bus) - */ + /* + * 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) && @@ -852,9 +882,11 @@ static int sd_dbg_s_register(struct gspca_dev *gspca_dev, index = reg->reg; value = reg->val; - /* Note that there shall be no access to other page - by any other function between the page swith and - the actual register write */ + /* + * Note that there shall be no access to other page + * by any other function between the page switch and + * the actual register write. + */ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */ reg_w(gspca_dev, index, value); @@ -940,6 +972,7 @@ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x2625)}, {USB_DEVICE(0x093a, 0x2626)}, + {USB_DEVICE(0x093a, 0x2627), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x2628)}, {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP}, {USB_DEVICE(0x093a, 0x262a)}, diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c index 1ac111176ffa..2cb7d95f7be7 100644 --- a/drivers/media/video/gspca/pac7311.c +++ b/drivers/media/video/gspca/pac7311.c @@ -20,34 +20,42 @@ */ /* 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. -*/ + * + * 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 Compression balance, interesting settings: + * 0x01 Use this to allow the camera to switch to higher compr. + * on the fly. Needed to stay within bandwidth @ 640x480@30 + * 0x1c From usb captures under Windows for 640x480 + * 0x2a Values >= this switch the camera to a lower compression, + * using the same table for both luminance and chrominance. + * This gives a sharper picture. Usable only at 640x480@ < + * 15 fps or 320x240 / 160x120. Note currently the driver + * does not use this as the quality gain is small and the + * generated JPG-s are only understood by v4l-utils >= 0.8.9 + * 0x3f From usb captures under Windows for 320x240 + * 0x69 From usb captures under Windows for 160x120 + * + * Register page 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 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 + * Note setting vflip disabled leads to a much lower image quality, + * so we always vflip, and tell userspace to flip it back + * 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. + */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -55,21 +63,21 @@ #include #include "gspca.h" +/* Include pac common sof detection functions */ +#include "pac_common.h" + +#define PAC7311_GAIN_DEFAULT 122 +#define PAC7311_EXPOSURE_DEFAULT 3 /* 20 fps, avoid using high compr. */ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li"); MODULE_DESCRIPTION("Pixart PAC7311"); MODULE_LICENSE("GPL"); -/* specific webcam descriptor for pac7311 */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - unsigned char contrast; - unsigned char gain; - unsigned char exposure; - unsigned char autogain; - __u8 hflip; - __u8 vflip; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *hflip; u8 sof_read; u8 autogain_ignore_frames; @@ -77,114 +85,6 @@ struct sd { atomic_t avg_lum; }; -/* V4L2 controls supported by the driver */ -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_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 const struct ctrl sd_ctrls[] = { -/* 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, - }, -/* 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[] = { {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -206,8 +106,8 @@ static const struct v4l2_pix_format vga_mode[] = { #define LOAD_PAGE4 254 #define END_OF_SEQUENCE 0 -/* pac 7311 */ static const __u8 init_7311[] = { + 0xff, 0x01, 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ 0x78, 0x40, /* Bit_0=start stream, Bit_6=LED */ 0x78, 0x44, /* Bit_0=start stream, Bit_6=LED */ @@ -387,90 +287,73 @@ static void reg_w_var(struct gspca_dev *gspca_dev, 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; - cam = &gspca_dev->cam; - - PDEBUG(D_CONF, "Find Sensor PAC7311"); cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); + cam->input_flags = V4L2_IN_ST_VFLIP; - sd->contrast = CONTRAST_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 pac7311 only */ -static void setcontrast(struct gspca_dev *gspca_dev) +static void setcontrast(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - reg_w(gspca_dev, 0xff, 0x04); - reg_w(gspca_dev, 0x10, sd->contrast >> 4); + reg_w(gspca_dev, 0x10, val); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } -static void setgain(struct gspca_dev *gspca_dev) +static void setgain(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - 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); + reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } -static void setexposure(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - __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; - reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - reg_w(gspca_dev, 0x02, reg); + reg_w(gspca_dev, 0x02, val); - /* 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 */ + /* load registers to sensor (Bit 0, auto clear) */ + reg_w(gspca_dev, 0x11, 0x01); + + /* + * Page 1 register 8 must always be 0x08 except when not in + * 640x480 mode and page 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) { + if (gspca_dev->width != 640 && val <= 3) reg_w(gspca_dev, 0x08, 0x09); - } else { + else reg_w(gspca_dev, 0x08, 0x08); - } + + /* + * Page1 register 80 sets the compression balance, normally we + * want / use 0x1c, but for 640x480@30fps we must allow the + * camera to use higher compression or we may run out of + * bandwidth. + */ + if (gspca_dev->width == 640 && val == 2) + reg_w(gspca_dev, 0x80, 0x01); + else + reg_w(gspca_dev, 0x80, 0x1c); /* load registers to sensor (Bit 0, auto clear) */ reg_w(gspca_dev, 0x11, 0x01); } -static void sethvflip(struct gspca_dev *gspca_dev) +static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) { - struct sd *sd = (struct sd *) gspca_dev; __u8 data; reg_w(gspca_dev, 0xff, 0x04); /* page 4 */ - data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00); + data = (hflip ? 0x04 : 0x00) | + (vflip ? 0x08 : 0x00); reg_w(gspca_dev, 0x21, data); /* load registers to sensor (Bit 0, auto clear) */ @@ -484,6 +367,82 @@ static int sd_init(struct gspca_dev *gspca_dev) return gspca_dev->usb_err; } +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->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. */ + gspca_dev->exposure->val = PAC7311_EXPOSURE_DEFAULT; + gspca_dev->gain->val = PAC7311_GAIN_DEFAULT; + sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + setcontrast(gspca_dev, ctrl->val); + break; + case V4L2_CID_AUTOGAIN: + if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val)) + setexposure(gspca_dev, gspca_dev->exposure->val); + if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val)) + setgain(gspca_dev, gspca_dev->gain->val); + break; + case V4L2_CID_HFLIP: + sethvflip(gspca_dev, sd->hflip->val, 1); + break; + default: + return -EINVAL; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +/* this function is called at probe time */ +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 4); + + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 7); + gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 2, 63, 1, + PAC7311_EXPOSURE_DEFAULT); + gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 244, 1, + PAC7311_GAIN_DEFAULT); + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + + v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false); + return 0; +} + +/* -- start the camera -- */ static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -492,19 +451,19 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w_var(gspca_dev, start_7311, page4_7311, sizeof(page4_7311)); - setcontrast(gspca_dev); - setgain(gspca_dev); - setexposure(gspca_dev); - sethvflip(gspca_dev); + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast)); + setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain)); + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure)); + sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1); /* set correct resolution */ switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) { - case 2: /* 160x120 pac7311 */ + case 2: /* 160x120 */ reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x17, 0x20); reg_w(gspca_dev, 0x87, 0x10); break; - case 1: /* 320x240 pac7311 */ + case 1: /* 320x240 */ reg_w(gspca_dev, 0xff, 0x01); reg_w(gspca_dev, 0x17, 0x30); reg_w(gspca_dev, 0x87, 0x11); @@ -541,14 +500,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev) 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) -{ -} - -/* 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; @@ -558,13 +509,13 @@ static void do_autogain(struct gspca_dev *gspca_dev) if (avg_lum == -1) return; - desired_lum = 200; + desired_lum = 170; deadzone = 20; 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)) + else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum, + desired_lum, deadzone)) sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES; } @@ -628,10 +579,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, if (sof) { int n, lum_offset, footer_length; - /* 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 */ + /* + * 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; @@ -668,127 +621,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -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) - setcontrast(gspca_dev); - return gspca_dev->usb_err; -} - -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_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 gspca_dev->usb_err; -} - -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 gspca_dev->usb_err; -} - -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 gspca_dev->usb_err; -} - -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 gspca_dev->usb_err; -} - -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 gspca_dev->usb_err; -} - -static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - *val = sd->vflip; - return 0; -} - #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, /* interrupt packet data */ @@ -820,16 +652,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, } #endif -/* sub-driver description for pac7311 */ static const struct sd_desc sd_desc = { .name = MODULE_NAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, - .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, .dq_callback = do_autogain, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c index 7e71aa2d2522..ad098202d7f0 100644 --- a/drivers/media/video/gspca/sn9c20x.c +++ b/drivers/media/video/gspca/sn9c20x.c @@ -59,35 +59,38 @@ MODULE_LICENSE("GPL"); #define SENSOR_MT9M111 9 #define SENSOR_MT9M112 10 #define SENSOR_HV7131R 11 -#define SENSOR_MT9VPRB 20 +#define SENSOR_MT9VPRB 12 /* camera flags */ #define HAS_NO_BUTTON 0x1 #define LED_REVERSE 0x2 /* some cameras unset gpio to turn on leds */ #define FLIP_DETECT 0x4 -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - SATURATION, - HUE, - GAMMA, - BLUE, - RED, - VFLIP, - HFLIP, - EXPOSURE, - GAIN, - AUTOGAIN, - QUALITY, - NCTRLS /* number of controls */ -}; - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; - struct gspca_ctrl ctrls[NCTRLS]; + struct { /* color control cluster */ + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + struct v4l2_ctrl *saturation; + struct v4l2_ctrl *hue; + }; + struct { /* blue/red balance control cluster */ + struct v4l2_ctrl *blue; + struct v4l2_ctrl *red; + }; + struct { /* h/vflip control cluster */ + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + }; + struct v4l2_ctrl *gamma; + struct { /* autogain and exposure or gain control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *gain; + }; + struct v4l2_ctrl *jpegqual; struct work_struct work; struct workqueue_struct *work_thread; @@ -105,6 +108,7 @@ struct sd { u8 exposure_step; u8 i2c_addr; + u8 i2c_intf; u8 sensor; u8 hstart; u8 vstart; @@ -166,175 +170,6 @@ static const struct dmi_system_id flip_dmi_table[] = { {} }; -static void set_cmatrix(struct gspca_dev *gspca_dev); -static void set_gamma(struct gspca_dev *gspca_dev); -static void set_redblue(struct gspca_dev *gspca_dev); -static void set_hvflip(struct gspca_dev *gspca_dev); -static void set_exposure(struct gspca_dev *gspca_dev); -static void set_gain(struct gspca_dev *gspca_dev); -static void set_quality(struct gspca_dev *gspca_dev); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[SATURATION] = { - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x7f - }, - .set_control = set_cmatrix - }, -[HUE] = { - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = -180, - .maximum = 180, - .step = 1, - .default_value = 0 - }, - .set_control = set_cmatrix - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 0, - .maximum = 0xff, - .step = 1, - .default_value = 0x10 - }, - .set_control = set_gamma - }, -[BLUE] = { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x28 - }, - .set_control = set_redblue - }, -[RED] = { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = 0, - .maximum = 0x7f, - .step = 1, - .default_value = 0x28 - }, - .set_control = set_redblue - }, -[HFLIP] = { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = set_hvflip - }, -[VFLIP] = { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0, - }, - .set_control = set_hvflip - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 0x1780, - .step = 1, - .default_value = 0x33, - }, - .set_control = set_exposure - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 28, - .step = 1, - .default_value = 0, - }, - .set_control = set_gain - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - }, -[QUALITY] = { - { - .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Compression Quality", -#define QUALITY_MIN 50 -#define QUALITY_MAX 90 -#define QUALITY_DEF 80 - .minimum = QUALITY_MIN, - .maximum = QUALITY_MAX, - .step = 1, - .default_value = QUALITY_DEF, - }, - .set_control = set_quality - }, -}; - static const struct v4l2_pix_format vga_mode[] = { {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 160, @@ -747,7 +582,7 @@ static const s16 hsv_blue_y[] = { 4, 2, 0, -1, -3, -5, -7, -9, -11 }; -static u16 i2c_ident[] = { +static const u16 i2c_ident[] = { V4L2_IDENT_OV9650, V4L2_IDENT_OV9655, V4L2_IDENT_SOI968, @@ -760,9 +595,10 @@ static u16 i2c_ident[] = { V4L2_IDENT_MT9M111, V4L2_IDENT_MT9M112, V4L2_IDENT_HV7131R, +[SENSOR_MT9VPRB] = V4L2_IDENT_UNKNOWN, }; -static u16 bridge_init[][2] = { +static const u16 bridge_init[][2] = { {0x1000, 0x78}, {0x1001, 0x40}, {0x1002, 0x1c}, {0x1020, 0x80}, {0x1061, 0x01}, {0x1067, 0x40}, {0x1068, 0x30}, {0x1069, 0x20}, {0x106a, 0x10}, @@ -786,7 +622,7 @@ static u16 bridge_init[][2] = { }; /* Gain = (bit[3:0] / 16 + 1) * (bit[4] + 1) * (bit[5] + 1) * (bit[6] + 1) */ -static u8 ov_gain[] = { +static const u8 ov_gain[] = { 0x00 /* 1x */, 0x04 /* 1.25x */, 0x08 /* 1.5x */, 0x0c /* 1.75x */, 0x10 /* 2x */, 0x12 /* 2.25x */, 0x14 /* 2.5x */, 0x16 /* 2.75x */, 0x18 /* 3x */, 0x1a /* 3.25x */, 0x1c /* 3.5x */, 0x1e /* 3.75x */, @@ -798,7 +634,7 @@ static u8 ov_gain[] = { }; /* Gain = (bit[8] + 1) * (bit[7] + 1) * (bit[6:0] * 0.03125) */ -static u16 micron1_gain[] = { +static const u16 micron1_gain[] = { /* 1x 1.25x 1.5x 1.75x */ 0x0020, 0x0028, 0x0030, 0x0038, /* 2x 2.25x 2.5x 2.75x */ @@ -819,7 +655,7 @@ static u16 micron1_gain[] = { /* mt9m001 sensor uses a different gain formula then other micron sensors */ /* Gain = (bit[6] + 1) * (bit[5-0] * 0.125) */ -static u16 micron2_gain[] = { +static const u16 micron2_gain[] = { /* 1x 1.25x 1.5x 1.75x */ 0x0008, 0x000a, 0x000c, 0x000e, /* 2x 2.25x 2.5x 2.75x */ @@ -839,7 +675,7 @@ static u16 micron2_gain[] = { }; /* Gain = .5 + bit[7:0] / 16 */ -static u8 hv7131r_gain[] = { +static const u8 hv7131r_gain[] = { 0x08 /* 1x */, 0x0c /* 1.25x */, 0x10 /* 1.5x */, 0x14 /* 1.75x */, 0x18 /* 2x */, 0x1c /* 2.25x */, 0x20 /* 2.5x */, 0x24 /* 2.75x */, 0x28 /* 3x */, 0x2c /* 3.25x */, 0x30 /* 3.5x */, 0x34 /* 3.75x */, @@ -850,7 +686,7 @@ static u8 hv7131r_gain[] = { 0x78 /* 8x */ }; -static struct i2c_reg_u8 soi968_init[] = { +static const struct i2c_reg_u8 soi968_init[] = { {0x0c, 0x00}, {0x0f, 0x1f}, {0x11, 0x80}, {0x38, 0x52}, {0x1e, 0x00}, {0x33, 0x08}, {0x35, 0x8c}, {0x36, 0x0c}, @@ -864,7 +700,7 @@ static struct i2c_reg_u8 soi968_init[] = { {0x00, 0x00}, {0x01, 0x80}, {0x02, 0x80}, }; -static struct i2c_reg_u8 ov7660_init[] = { +static const struct i2c_reg_u8 ov7660_init[] = { {0x0e, 0x80}, {0x0d, 0x08}, {0x0f, 0xc3}, {0x04, 0xc3}, {0x10, 0x40}, {0x11, 0x40}, {0x12, 0x05}, {0x13, 0xba}, {0x14, 0x2a}, @@ -872,11 +708,11 @@ static struct i2c_reg_u8 ov7660_init[] = { 0x10, 0x61 and sd->hstart, vstart = 3, fixes ugly colored borders */ {0x17, 0x10}, {0x18, 0x61}, {0x37, 0x0f}, {0x38, 0x02}, {0x39, 0x43}, - {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0xf6}, - {0x2e, 0x0b}, {0x01, 0x78}, {0x02, 0x50}, + {0x3a, 0x00}, {0x69, 0x90}, {0x2d, 0x00}, + {0x2e, 0x00}, {0x01, 0x78}, {0x02, 0x50}, }; -static struct i2c_reg_u8 ov7670_init[] = { +static const struct i2c_reg_u8 ov7670_init[] = { {0x11, 0x80}, {0x3a, 0x04}, {0x12, 0x01}, {0x32, 0xb6}, {0x03, 0x0a}, {0x0c, 0x00}, {0x3e, 0x00}, {0x70, 0x3a}, {0x71, 0x35}, {0x72, 0x11}, {0x73, 0xf0}, @@ -933,7 +769,7 @@ static struct i2c_reg_u8 ov7670_init[] = { {0x93, 0x00}, }; -static struct i2c_reg_u8 ov9650_init[] = { +static const struct i2c_reg_u8 ov9650_init[] = { {0x00, 0x00}, {0x01, 0x78}, {0x02, 0x78}, {0x03, 0x36}, {0x04, 0x03}, {0x05, 0x00}, {0x06, 0x00}, {0x08, 0x00}, @@ -963,7 +799,7 @@ static struct i2c_reg_u8 ov9650_init[] = { {0xaa, 0x92}, {0xab, 0x0a}, }; -static struct i2c_reg_u8 ov9655_init[] = { +static const struct i2c_reg_u8 ov9655_init[] = { {0x0e, 0x61}, {0x11, 0x80}, {0x13, 0xba}, {0x14, 0x2e}, {0x16, 0x24}, {0x1e, 0x04}, {0x27, 0x08}, {0x28, 0x08}, {0x29, 0x15}, {0x2c, 0x08}, {0x34, 0x3d}, @@ -990,7 +826,7 @@ static struct i2c_reg_u8 ov9655_init[] = { {0x04, 0x03}, {0x00, 0x13}, }; -static struct i2c_reg_u16 mt9v112_init[] = { +static const struct i2c_reg_u16 mt9v112_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0020}, {0x34, 0xc019}, {0x0a, 0x0011}, {0x0b, 0x000b}, {0x20, 0x0703}, {0x35, 0x2022}, {0xf0, 0x0001}, @@ -1009,7 +845,7 @@ static struct i2c_reg_u16 mt9v112_init[] = { {0x2c, 0x00ae}, {0x2d, 0x00ae}, {0x2e, 0x00ae}, }; -static struct i2c_reg_u16 mt9v111_init[] = { +static const struct i2c_reg_u16 mt9v111_init[] = { {0x01, 0x0004}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0001}, {0x05, 0x0004}, {0x2d, 0xe0a0}, {0x2e, 0x0c64}, {0x2f, 0x0064}, {0x06, 0x600e}, @@ -1019,7 +855,7 @@ static struct i2c_reg_u16 mt9v111_init[] = { {0x0e, 0x0008}, {0x20, 0x0000} }; -static struct i2c_reg_u16 mt9v011_init[] = { +static const struct i2c_reg_u16 mt9v011_init[] = { {0x07, 0x0002}, {0x0d, 0x0001}, {0x0d, 0x0000}, {0x01, 0x0008}, {0x02, 0x0016}, {0x03, 0x01e1}, {0x04, 0x0281}, {0x05, 0x0083}, {0x06, 0x0006}, @@ -1046,7 +882,7 @@ static struct i2c_reg_u16 mt9v011_init[] = { {0x06, 0x0029}, {0x05, 0x0009}, }; -static struct i2c_reg_u16 mt9m001_init[] = { +static const struct i2c_reg_u16 mt9m001_init[] = { {0x0d, 0x0001}, {0x0d, 0x0000}, {0x04, 0x0500}, /* hres = 1280 */ @@ -1062,21 +898,21 @@ static struct i2c_reg_u16 mt9m001_init[] = { {0x35, 0x0057}, }; -static struct i2c_reg_u16 mt9m111_init[] = { +static const struct i2c_reg_u16 mt9m111_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, {0xf0, 0x0000}, }; -static struct i2c_reg_u16 mt9m112_init[] = { +static const struct i2c_reg_u16 mt9m112_init[] = { {0xf0, 0x0000}, {0x0d, 0x0021}, {0x0d, 0x0008}, {0xf0, 0x0001}, {0x3a, 0x4300}, {0x9b, 0x4300}, {0x06, 0x708e}, {0xf0, 0x0002}, {0x2e, 0x0a1e}, {0xf0, 0x0000}, }; -static struct i2c_reg_u8 hv7131r_init[] = { +static const struct i2c_reg_u8 hv7131r_init[] = { {0x02, 0x08}, {0x02, 0x00}, {0x01, 0x08}, {0x02, 0x00}, {0x20, 0x00}, {0x21, 0xd0}, {0x22, 0x00}, {0x23, 0x09}, {0x01, 0x08}, @@ -1167,7 +1003,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) * from the point of view of the bridge, the length * includes the address */ - row[0] = 0x81 | (2 << 4); + row[0] = sd->i2c_intf | (2 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = val; @@ -1180,7 +1016,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val) } static void i2c_w1_buf(struct gspca_dev *gspca_dev, - struct i2c_reg_u8 *buf, int sz) + const struct i2c_reg_u8 *buf, int sz) { while (--sz >= 0) { i2c_w1(gspca_dev, buf->reg, buf->val); @@ -1197,7 +1033,7 @@ static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) * from the point of view of the bridge, the length * includes the address */ - row[0] = 0x81 | (3 << 4); + row[0] = sd->i2c_intf | (3 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = val >> 8; @@ -1210,7 +1046,7 @@ static void i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val) } static void i2c_w2_buf(struct gspca_dev *gspca_dev, - struct i2c_reg_u16 *buf, int sz) + const struct i2c_reg_u16 *buf, int sz) { while (--sz >= 0) { i2c_w2(gspca_dev, buf->reg, buf->val); @@ -1223,7 +1059,7 @@ static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | (1 << 4); + row[0] = sd->i2c_intf | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1232,7 +1068,7 @@ static void i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val) row[6] = 0; row[7] = 0x10; i2c_w(gspca_dev, row); - row[0] = 0x81 | (1 << 4) | 0x02; + row[0] = sd->i2c_intf | (1 << 4) | 0x02; row[2] = 0; i2c_w(gspca_dev, row); reg_r(gspca_dev, 0x10c2, 5); @@ -1244,7 +1080,7 @@ static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) struct sd *sd = (struct sd *) gspca_dev; u8 row[8]; - row[0] = 0x81 | (1 << 4); + row[0] = sd->i2c_intf | (1 << 4); row[1] = sd->i2c_addr; row[2] = reg; row[3] = 0; @@ -1253,7 +1089,7 @@ static void i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val) row[6] = 0; row[7] = 0x10; i2c_w(gspca_dev, row); - row[0] = 0x81 | (2 << 4) | 0x02; + row[0] = sd->i2c_intf | (2 << 4) | 0x02; row[2] = 0; i2c_w(gspca_dev, row); reg_r(gspca_dev, 0x10c2, 5); @@ -1294,8 +1130,6 @@ static void ov9655_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("OV9655 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 2; } @@ -1310,9 +1144,6 @@ static void soi968_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("SOI968 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP) - | (1 << EXPOSURE); sd->hstart = 60; sd->vstart = 11; } @@ -1340,8 +1171,6 @@ static void ov7670_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("OV7670 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 0; sd->vstart = 1; } @@ -1378,9 +1207,6 @@ static void mt9v_init_sensor(struct gspca_dev *gspca_dev) pr_err("MT9V111 sensor initialization failed\n"); return; } - gspca_dev->ctrl_dis = (1 << EXPOSURE) - | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 2; sd->vstart = 2; sd->sensor = SENSOR_MT9V111; @@ -1422,8 +1248,6 @@ static void mt9m112_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M112 sensor initialization failed\n"); - gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; } @@ -1436,8 +1260,6 @@ static void mt9m111_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M111 sensor initialization failed\n"); - gspca_dev->ctrl_dis = (1 << EXPOSURE) | (1 << AUTOGAIN) - | (1 << GAIN); sd->hstart = 0; sd->vstart = 2; } @@ -1470,8 +1292,6 @@ static void mt9m001_init_sensor(struct gspca_dev *gspca_dev) if (gspca_dev->usb_err < 0) pr_err("MT9M001 sensor initialization failed\n"); - /* disable hflip and vflip */ - gspca_dev->ctrl_dis = (1 << HFLIP) | (1 << VFLIP); sd->hstart = 1; sd->vstart = 1; } @@ -1488,20 +1308,18 @@ static void hv7131r_init_sensor(struct gspca_dev *gspca_dev) sd->vstart = 1; } -static void set_cmatrix(struct gspca_dev *gspca_dev) +static void set_cmatrix(struct gspca_dev *gspca_dev, + s32 brightness, s32 contrast, s32 satur, s32 hue) { - struct sd *sd = (struct sd *) gspca_dev; - int satur; - s32 hue_coord, hue_index = 180 + sd->ctrls[HUE].val; + s32 hue_coord, hue_index = 180 + hue; u8 cmatrix[21]; memset(cmatrix, 0, sizeof cmatrix); - cmatrix[2] = (sd->ctrls[CONTRAST].val * 0x25 / 0x100) + 0x26; + cmatrix[2] = (contrast * 0x25 / 0x100) + 0x26; cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25; cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25; - cmatrix[18] = sd->ctrls[BRIGHTNESS].val - 0x80; + cmatrix[18] = brightness - 0x80; - satur = sd->ctrls[SATURATION].val; hue_coord = (hsv_red_x[hue_index] * satur) >> 8; cmatrix[6] = hue_coord; cmatrix[7] = (hue_coord >> 8) & 0x0f; @@ -1529,11 +1347,10 @@ static void set_cmatrix(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x10e1, cmatrix, 21); } -static void set_gamma(struct gspca_dev *gspca_dev) +static void set_gamma(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; u8 gamma[17]; - u8 gval = sd->ctrls[GAMMA].val * 0xb8 / 0x100; + u8 gval = val * 0xb8 / 0x100; gamma[0] = 0x0a; gamma[1] = 0x13 + (gval * (0xcb - 0x13) / 0xb8); @@ -1556,26 +1373,21 @@ static void set_gamma(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x1190, gamma, 17); } -static void set_redblue(struct gspca_dev *gspca_dev) +static void set_redblue(struct gspca_dev *gspca_dev, s32 blue, s32 red) { - struct sd *sd = (struct sd *) gspca_dev; - - reg_w1(gspca_dev, 0x118c, sd->ctrls[RED].val); - reg_w1(gspca_dev, 0x118f, sd->ctrls[BLUE].val); + reg_w1(gspca_dev, 0x118c, red); + reg_w1(gspca_dev, 0x118f, blue); } -static void set_hvflip(struct gspca_dev *gspca_dev) +static void set_hvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip) { - u8 value, tslb, hflip, vflip; + u8 value, tslb; u16 value2; struct sd *sd = (struct sd *) gspca_dev; if ((sd->flags & FLIP_DETECT) && dmi_check_system(flip_dmi_table)) { - hflip = !sd->ctrls[HFLIP].val; - vflip = !sd->ctrls[VFLIP].val; - } else { - hflip = sd->ctrls[HFLIP].val; - vflip = sd->ctrls[VFLIP].val; + hflip = !hflip; + vflip = !vflip; } switch (sd->sensor) { @@ -1638,20 +1450,38 @@ static void set_hvflip(struct gspca_dev *gspca_dev) } } -static void set_exposure(struct gspca_dev *gspca_dev) +static void set_exposure(struct gspca_dev *gspca_dev, s32 expo) { struct sd *sd = (struct sd *) gspca_dev; - u8 exp[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e}; - int expo; + u8 exp[8] = {sd->i2c_intf, sd->i2c_addr, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + int expo2; + + if (gspca_dev->streaming) + exp[7] = 0x1e; - expo = sd->ctrls[EXPOSURE].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: case SENSOR_OV9655: case SENSOR_OV9650: + if (expo > 547) + expo2 = 547; + else + expo2 = expo; + exp[0] |= (2 << 4); + exp[2] = 0x10; /* AECH */ + exp[3] = expo2 >> 2; + exp[7] = 0x10; + i2c_w(gspca_dev, exp); + exp[2] = 0x04; /* COM1 */ + exp[3] = expo2 & 0x0003; + exp[7] = 0x10; + i2c_w(gspca_dev, exp); + expo -= expo2; + exp[7] = 0x1e; exp[0] |= (3 << 4); - exp[2] = 0x2d; + exp[2] = 0x2d; /* ADVFL & ADVFH */ exp[3] = expo; exp[4] = expo >> 8; break; @@ -1676,13 +1506,15 @@ static void set_exposure(struct gspca_dev *gspca_dev) i2c_w(gspca_dev, exp); } -static void set_gain(struct gspca_dev *gspca_dev) +static void set_gain(struct gspca_dev *gspca_dev, s32 g) { struct sd *sd = (struct sd *) gspca_dev; - u8 gain[8] = {0x81, sd->i2c_addr, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1d}; - int g; + u8 gain[8] = {sd->i2c_intf, sd->i2c_addr, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x10}; + + if (gspca_dev->streaming) + gain[7] = 0x15; /* or 1d ? */ - g = sd->ctrls[GAIN].val; switch (sd->sensor) { case SENSOR_OV7660: case SENSOR_OV7670: @@ -1721,11 +1553,11 @@ static void set_gain(struct gspca_dev *gspca_dev) i2c_w(gspca_dev, gain); } -static void set_quality(struct gspca_dev *gspca_dev) +static void set_quality(struct gspca_dev *gspca_dev, s32 val) { struct sd *sd = (struct sd *) gspca_dev; - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + jpeg_set_qual(sd->jpeg_hdr, val); reg_w1(gspca_dev, 0x1061, 0x01); /* stop transfer */ reg_w1(gspca_dev, 0x10e0, sd->fmt | 0x20); /* write QTAB */ reg_w(gspca_dev, 0x1100, &sd->jpeg_hdr[JPEG_QT0_OFFSET], 64); @@ -1827,6 +1659,7 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->sensor = id->driver_info >> 8; sd->i2c_addr = id->driver_info; sd->flags = id->driver_info >> 16; + sd->i2c_intf = 0x80; /* i2c 100 Kb/s */ switch (sd->sensor) { case SENSOR_MT9M112: @@ -1840,6 +1673,9 @@ static int sd_config(struct gspca_dev *gspca_dev, cam->cam_mode = mono_mode; cam->nmodes = ARRAY_SIZE(mono_mode); break; + case SENSOR_HV7131R: + sd->i2c_intf = 0x81; /* i2c 400 Kb/s */ + /* fall thru */ default: cam->cam_mode = vga_mode; cam->nmodes = ARRAY_SIZE(vga_mode); @@ -1850,13 +1686,133 @@ static int sd_config(struct gspca_dev *gspca_dev, sd->older_step = 0; sd->exposure_step = 16; - gspca_dev->cam.ctrls = sd->ctrls; - INIT_WORK(&sd->work, qual_upd); return 0; } +static int sd_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + gspca_dev->usb_err = 0; + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* color control cluster */ + case V4L2_CID_BRIGHTNESS: + set_cmatrix(gspca_dev, sd->brightness->val, + sd->contrast->val, sd->saturation->val, sd->hue->val); + break; + case V4L2_CID_GAMMA: + set_gamma(gspca_dev, ctrl->val); + break; + /* blue/red balance cluster */ + case V4L2_CID_BLUE_BALANCE: + set_redblue(gspca_dev, sd->blue->val, sd->red->val); + break; + /* h/vflip cluster */ + case V4L2_CID_HFLIP: + set_hvflip(gspca_dev, sd->hflip->val, sd->vflip->val); + break; + /* standalone exposure control */ + case V4L2_CID_EXPOSURE: + set_exposure(gspca_dev, ctrl->val); + break; + /* standalone gain control */ + case V4L2_CID_GAIN: + set_gain(gspca_dev, ctrl->val); + break; + /* autogain + exposure or gain control cluster */ + case V4L2_CID_AUTOGAIN: + if (sd->sensor == SENSOR_SOI968) + set_gain(gspca_dev, sd->gain->val); + else + set_exposure(gspca_dev, sd->exposure->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + set_quality(gspca_dev, ctrl->val); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops sd_ctrl_ops = { + .s_ctrl = sd_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 13); + + sd->brightness = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 127); + sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 127); + sd->saturation = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 127); + sd->hue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HUE, -180, 180, 1, 0); + v4l2_ctrl_cluster(4, &sd->brightness); + + sd->gamma = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAMMA, 0, 255, 1, 0x10); + + sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_BLUE_BALANCE, 0, 127, 1, 0x28); + sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_RED_BALANCE, 0, 127, 1, 0x28); + v4l2_ctrl_cluster(2, &sd->blue); + + if (sd->sensor != SENSOR_OV9655 && sd->sensor != SENSOR_SOI968 && + sd->sensor != SENSOR_OV7670 && sd->sensor != SENSOR_MT9M001 && + sd->sensor != SENSOR_MT9VPRB) { + sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sd->vflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_cluster(2, &sd->hflip); + } + + if (sd->sensor != SENSOR_SOI968 && sd->sensor != SENSOR_MT9VPRB && + sd->sensor != SENSOR_MT9M112 && sd->sensor != SENSOR_MT9M111 && + sd->sensor != SENSOR_MT9V111) + sd->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0x1780, 1, 0x33); + + if (sd->sensor != SENSOR_MT9VPRB && sd->sensor != SENSOR_MT9M112 && + sd->sensor != SENSOR_MT9M111 && sd->sensor != SENSOR_MT9V111) { + sd->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_GAIN, 0, 28, 1, 0); + sd->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (sd->sensor == SENSOR_SOI968) + /* this sensor doesn't have the exposure control and + autogain is clustered with gain instead. This works + because sd->exposure == NULL. */ + v4l2_ctrl_auto_cluster(3, &sd->autogain, 0, false); + else + /* Otherwise autogain is clustered with exposure. */ + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, false); + } + + sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 50, 90, 1, 80); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + return 0; +} + static int sd_init(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -1949,7 +1905,6 @@ static int sd_init(struct gspca_dev *gspca_dev) pr_err("Unsupported sensor\n"); gspca_dev->usb_err = -ENODEV; } - return gspca_dev->usb_err; } @@ -2025,8 +1980,8 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev) if (intf->num_altsetting != 9) { pr_warn("sn9c20x camera with unknown number of alt " - "settings (%d), please report!\n", - intf->num_altsetting); + "settings (%d), please report!\n", + intf->num_altsetting); gspca_dev->alt = intf->num_altsetting; return 0; } @@ -2067,7 +2022,7 @@ static int sd_start(struct gspca_dev *gspca_dev) jpeg_define(sd->jpeg_hdr, height, width, 0x21); - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); + jpeg_set_qual(sd->jpeg_hdr, v4l2_ctrl_g_ctrl(sd->jpegqual)); if (mode & MODE_RAW) fmt = 0x2d; @@ -2104,12 +2059,17 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w1(gspca_dev, 0x1189, scale); reg_w1(gspca_dev, 0x10e0, fmt); - set_cmatrix(gspca_dev); - set_gamma(gspca_dev); - set_redblue(gspca_dev); - set_gain(gspca_dev); - set_exposure(gspca_dev); - set_hvflip(gspca_dev); + set_cmatrix(gspca_dev, v4l2_ctrl_g_ctrl(sd->brightness), + v4l2_ctrl_g_ctrl(sd->contrast), + v4l2_ctrl_g_ctrl(sd->saturation), + v4l2_ctrl_g_ctrl(sd->hue)); + set_gamma(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma)); + set_redblue(gspca_dev, v4l2_ctrl_g_ctrl(sd->blue), + v4l2_ctrl_g_ctrl(sd->red)); + set_gain(gspca_dev, v4l2_ctrl_g_ctrl(sd->gain)); + set_exposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); + set_hvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), + v4l2_ctrl_g_ctrl(sd->vflip)); reg_w1(gspca_dev, 0x1007, 0x20); reg_w1(gspca_dev, 0x1061, 0x03); @@ -2148,6 +2108,9 @@ static void sd_stop0(struct gspca_dev *gspca_dev) static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; + s32 cur_exp = v4l2_ctrl_g_ctrl(sd->exposure); + s32 max = sd->exposure->maximum - sd->exposure_step; + s32 min = sd->exposure->minimum + sd->exposure_step; s16 new_exp; /* @@ -2156,16 +2119,15 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) * and exposure steps */ if (avg_lum < MIN_AVG_LUM) { - if (sd->ctrls[EXPOSURE].val > 0x1770) + if (cur_exp > max) return; - new_exp = sd->ctrls[EXPOSURE].val + sd->exposure_step; - if (new_exp > 0x1770) - new_exp = 0x1770; - if (new_exp < 0x10) - new_exp = 0x10; - sd->ctrls[EXPOSURE].val = new_exp; - set_exposure(gspca_dev); + new_exp = cur_exp + sd->exposure_step; + if (new_exp > max) + new_exp = max; + if (new_exp < min) + new_exp = min; + v4l2_ctrl_s_ctrl(sd->exposure, new_exp); sd->older_step = sd->old_step; sd->old_step = 1; @@ -2176,15 +2138,14 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) sd->exposure_step += 2; } if (avg_lum > MAX_AVG_LUM) { - if (sd->ctrls[EXPOSURE].val < 0x10) + if (cur_exp < min) return; - new_exp = sd->ctrls[EXPOSURE].val - sd->exposure_step; - if (new_exp > 0x1700) - new_exp = 0x1770; - if (new_exp < 0x10) - new_exp = 0x10; - sd->ctrls[EXPOSURE].val = new_exp; - set_exposure(gspca_dev); + new_exp = cur_exp - sd->exposure_step; + if (new_exp > max) + new_exp = max; + if (new_exp < min) + new_exp = min; + v4l2_ctrl_s_ctrl(sd->exposure, new_exp); sd->older_step = sd->old_step; sd->old_step = 0; @@ -2198,19 +2159,12 @@ static void do_autoexposure(struct gspca_dev *gspca_dev, u16 avg_lum) static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum) { struct sd *sd = (struct sd *) gspca_dev; + s32 cur_gain = v4l2_ctrl_g_ctrl(sd->gain); - if (avg_lum < MIN_AVG_LUM) { - if (sd->ctrls[GAIN].val + 1 <= 28) { - sd->ctrls[GAIN].val++; - set_gain(gspca_dev); - } - } - if (avg_lum > MAX_AVG_LUM) { - if (sd->ctrls[GAIN].val > 0) { - sd->ctrls[GAIN].val--; - set_gain(gspca_dev); - } - } + if (avg_lum < MIN_AVG_LUM && cur_gain < sd->gain->maximum) + v4l2_ctrl_s_ctrl(sd->gain, cur_gain + 1); + if (avg_lum > MAX_AVG_LUM && cur_gain > sd->gain->minimum) + v4l2_ctrl_s_ctrl(sd->gain, cur_gain - 1); } static void sd_dqcallback(struct gspca_dev *gspca_dev) @@ -2218,7 +2172,7 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev) struct sd *sd = (struct sd *) gspca_dev; int avg_lum; - if (!sd->ctrls[AUTOGAIN].val) + if (!v4l2_ctrl_g_ctrl(sd->autogain)) return; avg_lum = atomic_read(&sd->avg_lum); @@ -2234,10 +2188,11 @@ static void qual_upd(struct work_struct *work) { struct sd *sd = container_of(work, struct sd, work); struct gspca_dev *gspca_dev = &sd->gspca_dev; + s32 qual = v4l2_ctrl_g_ctrl(sd->jpegqual); mutex_lock(&gspca_dev->usb_lock); - PDEBUG(D_STREAM, "qual_upd %d%%", sd->ctrls[QUALITY].val); - set_quality(gspca_dev); + PDEBUG(D_STREAM, "qual_upd %d%%", qual); + set_quality(gspca_dev, qual); mutex_unlock(&gspca_dev->usb_lock); } @@ -2286,14 +2241,18 @@ static void transfer_check(struct gspca_dev *gspca_dev, if (new_qual != 0) { sd->nchg += new_qual; if (sd->nchg < -6 || sd->nchg >= 12) { + /* Note: we are in interrupt context, so we can't + use v4l2_ctrl_g/s_ctrl here. Access the value + directly instead. */ + s32 curqual = sd->jpegqual->cur.val; sd->nchg = 0; - new_qual += sd->ctrls[QUALITY].val; - if (new_qual < QUALITY_MIN) - new_qual = QUALITY_MIN; - else if (new_qual > QUALITY_MAX) - new_qual = QUALITY_MAX; - if (new_qual != sd->ctrls[QUALITY].val) { - sd->ctrls[QUALITY].val = new_qual; + new_qual += curqual; + if (new_qual < sd->jpegqual->minimum) + new_qual = sd->jpegqual->minimum; + else if (new_qual > sd->jpegqual->maximum) + new_qual = sd->jpegqual->maximum; + if (new_qual != curqual) { + sd->jpegqual->cur.val = new_qual; queue_work(sd->work_thread, &sd->work); } } @@ -2309,7 +2268,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, { struct sd *sd = (struct sd *) gspca_dev; int avg_lum, is_jpeg; - static u8 frame_header[] = + static const u8 frame_header[] = {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96}; is_jpeg = (sd->fmt & 0x03) == 0; @@ -2373,10 +2332,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* sub-driver description */ static const struct sd_desc sd_desc = { .name = KBUILD_MODNAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, .isoc_init = sd_isoc_init, .start = sd_start, .stopN = sd_stopN, diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c index 6a1148d7fe92..e2bdf8f632f4 100644 --- a/drivers/media/video/gspca/sonixb.c +++ b/drivers/media/video/gspca/sonixb.c @@ -1000,6 +1000,8 @@ static void setfreq(struct gspca_dev *gspca_dev) } } +#define WANT_REGULAR_AUTOGAIN +#define WANT_COARSE_EXPO_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c index 863c755dd2b7..4d1696d1a7f4 100644 --- a/drivers/media/video/gspca/sonixj.c +++ b/drivers/media/video/gspca/sonixj.c @@ -2800,10 +2800,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) } } -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt bridge -#define exp_too_high_cnt sensor - +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" static void do_autogain(struct gspca_dev *gspca_dev) diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c index 2fe3c29bd6b7..04f54654a026 100644 --- a/drivers/media/video/gspca/sq905.c +++ b/drivers/media/video/gspca/sq905.c @@ -232,7 +232,11 @@ static void sq905_dostream(struct work_struct *work) frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage + FRAME_HEADER_LEN; - while (gspca_dev->present && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif /* request some data and then read it until we have * a complete frame. */ bytes_left = frame_sz; @@ -242,7 +246,7 @@ static void sq905_dostream(struct work_struct *work) 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) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > SQ905_MAX_TRANSFER ? SQ905_MAX_TRANSFER : bytes_left; ret = sq905_read_data(gspca_dev, buffer, data_len, 1); @@ -274,7 +278,7 @@ static void sq905_dostream(struct work_struct *work) gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0); } - if (gspca_dev->present) { + if (gspca_dev->dev) { /* acknowledge the frame */ mutex_lock(&gspca_dev->usb_lock); ret = sq905_ack_frame(gspca_dev); @@ -284,7 +288,7 @@ static void sq905_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); sq905_command(gspca_dev, SQ905_CLEAR); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c index ae783634712f..f34ddb0570c8 100644 --- a/drivers/media/video/gspca/sq905c.c +++ b/drivers/media/video/gspca/sq905c.c @@ -150,7 +150,11 @@ static void sq905c_dostream(struct work_struct *work) goto quit_stream; } - while (gspca_dev->present && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif /* Request the header, which tells the size to download */ ret = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x81), @@ -169,7 +173,7 @@ static void sq905c_dostream(struct work_struct *work) packet_type = FIRST_PACKET; gspca_frame_add(gspca_dev, packet_type, buffer, FRAME_HEADER_LEN); - while (bytes_left > 0 && gspca_dev->present) { + while (bytes_left > 0 && gspca_dev->dev) { data_len = bytes_left > SQ905C_MAX_TRANSFER ? SQ905C_MAX_TRANSFER : bytes_left; ret = usb_bulk_msg(gspca_dev->dev, @@ -191,7 +195,7 @@ static void sq905c_dostream(struct work_struct *work) } } quit_stream: - if (gspca_dev->present) { + if (gspca_dev->dev) { mutex_lock(&gspca_dev->usb_lock); sq905c_command(gspca_dev, SQ905C_CLEAR, 0); mutex_unlock(&gspca_dev->usb_lock); diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c index 91d99b4cc57b..999ec7764449 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx.c @@ -261,6 +261,17 @@ static int stv06xx_init(struct gspca_dev *gspca_dev) return (err < 0) ? err : 0; } +/* this function is called at probe time */ +static int stv06xx_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + + PDEBUG(D_PROBE, "Initializing controls"); + + gspca_dev->vdev.ctrl_handler = &gspca_dev->ctrl_handler; + return sd->sensor->init_controls(sd); +} + /* Start the camera */ static int stv06xx_start(struct gspca_dev *gspca_dev) { @@ -512,6 +523,7 @@ static const struct sd_desc sd_desc = { .name = MODULE_NAME, .config = stv06xx_config, .init = stv06xx_init, + .init_controls = stv06xx_init_controls, .start = stv06xx_start, .stopN = stv06xx_stopN, .pkt_scan = stv06xx_pkt_scan, @@ -530,9 +542,8 @@ static int stv06xx_config(struct gspca_dev *gspca_dev, PDEBUG(D_PROBE, "Configuring camera"); - sd->desc = sd_desc; sd->bridge = id->driver_info; - gspca_dev->sd_desc = &sd->desc; + gspca_dev->sd_desc = &sd_desc; if (dump_bridge) stv06xx_dump_bridge(sd); @@ -594,11 +605,12 @@ static void sd_disconnect(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); struct sd *sd = (struct sd *) gspca_dev; + void *priv = sd->sensor_priv; PDEBUG(D_PROBE, "Disconnecting the stv06xx device"); - if (sd->sensor->disconnect) - sd->sensor->disconnect(sd); + sd->sensor = NULL; gspca_disconnect(intf); + kfree(priv); } static struct usb_driver sd_driver = { @@ -609,6 +621,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.h b/drivers/media/video/gspca/stv06xx/stv06xx.h index d270a5981afe..34957a4ec150 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx.h @@ -89,9 +89,6 @@ struct sd { /* A pointer to the currently connected sensor */ const struct stv06xx_sensor *sensor; - /* A pointer to the sd_desc struct */ - struct sd_desc desc; - /* Sensor private data */ void *sensor_priv; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c index a8698b7a7566..06fa54c5efb2 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c @@ -32,36 +32,6 @@ #include "stv06xx_hdcs.h" -static const struct ctrl hdcs1x00_ctrl[] = { - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_exposure, - .get = hdcs_get_exposure - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_gain, - .get = hdcs_get_gain - } -}; - static struct v4l2_pix_format hdcs1x00_mode[] = { { HDCS_1X00_DEF_WIDTH, @@ -76,36 +46,6 @@ static struct v4l2_pix_format hdcs1x00_mode[] = { } }; -static const struct ctrl hdcs1020_ctrl[] = { - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0x00, - .maximum = 0xffff, - .step = 0x1, - .default_value = HDCS_DEFAULT_EXPOSURE, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_exposure, - .get = hdcs_get_exposure - }, { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "gain", - .minimum = 0x00, - .maximum = 0xff, - .step = 0x1, - .default_value = HDCS_DEFAULT_GAIN, - .flags = V4L2_CTRL_FLAG_SLIDER - }, - .set = hdcs_set_gain, - .get = hdcs_get_gain - } -}; - static struct v4l2_pix_format hdcs1020_mode[] = { { HDCS_1020_DEF_WIDTH, @@ -150,7 +90,6 @@ struct hdcs { } exp; int psmp; - u8 exp_cache, gain_cache; }; static int hdcs_reg_write_seq(struct sd *sd, u8 reg, u8 *vals, u8 len) @@ -232,16 +171,6 @@ static int hdcs_reset(struct sd *sd) return err; } -static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct hdcs *hdcs = sd->sensor_priv; - - *val = hdcs->exp_cache; - - return 0; -} - static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { struct sd *sd = (struct sd *) gspca_dev; @@ -260,9 +189,6 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) int cycles, err; u8 exp[14]; - val &= 0xff; - hdcs->exp_cache = val; - cycles = val * HDCS_CLK_FREQ_MHZ * 257; ct = hdcs->exp.cto + hdcs->psmp + (HDCS_ADC_START_SIG_DUR + 2); @@ -336,12 +262,9 @@ static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val) static int hdcs_set_gains(struct sd *sd, u8 g) { - struct hdcs *hdcs = sd->sensor_priv; int err; u8 gains[4]; - hdcs->gain_cache = g; - /* the voltage gain Av = (1 + 19 * val / 127) * (1 + bit7) */ if (g > 127) g = 0x80 | (g / 2); @@ -352,17 +275,7 @@ static int hdcs_set_gains(struct sd *sd, u8 g) gains[3] = g; err = hdcs_reg_write_seq(sd, HDCS_ERECPGA, gains, 4); - return err; -} - -static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - struct hdcs *hdcs = sd->sensor_priv; - - *val = hdcs->gain_cache; - - return 0; + return err; } static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val) @@ -420,6 +333,39 @@ static int hdcs_set_size(struct sd *sd, return err; } +static int hdcs_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_GAIN: + err = hdcs_set_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = hdcs_set_exposure(gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops hdcs_ctrl_ops = { + .s_ctrl = hdcs_s_ctrl, +}; + +static int hdcs_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 2); + v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0xff, 1, HDCS_DEFAULT_EXPOSURE); + v4l2_ctrl_new_std(hdl, &hdcs_ctrl_ops, + V4L2_CID_GAIN, 0, 0xff, 1, HDCS_DEFAULT_GAIN); + return hdl->error; +} + static int hdcs_probe_1x00(struct sd *sd) { struct hdcs *hdcs; @@ -434,8 +380,6 @@ static int hdcs_probe_1x00(struct sd *sd) sd->gspca_dev.cam.cam_mode = hdcs1x00_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode); - sd->desc.ctrls = hdcs1x00_ctrl; - sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl); hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); if (!hdcs) @@ -493,8 +437,6 @@ static int hdcs_probe_1020(struct sd *sd) sd->gspca_dev.cam.cam_mode = hdcs1020_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode); - sd->desc.ctrls = hdcs1020_ctrl; - sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl); hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL); if (!hdcs) @@ -537,12 +479,6 @@ static int hdcs_stop(struct sd *sd) return hdcs_set_state(sd, HDCS_STATE_SLEEP); } -static void hdcs_disconnect(struct sd *sd) -{ - PDEBUG(D_PROBE, "Disconnecting the sensor"); - kfree(sd->sensor_priv); -} - static int hdcs_init(struct sd *sd) { struct hdcs *hdcs = sd->sensor_priv; @@ -587,16 +523,7 @@ static int hdcs_init(struct sd *sd) if (err < 0) return err; - err = hdcs_set_gains(sd, HDCS_DEFAULT_GAIN); - if (err < 0) - return err; - - err = hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); - if (err < 0) - return err; - - err = hdcs_set_exposure(&sd->gspca_dev, HDCS_DEFAULT_EXPOSURE); - return err; + return hdcs_set_size(sd, hdcs->array.width, hdcs->array.height); } static int hdcs_dump(struct sd *sd) diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h index a14a84a5079b..1ba9158d0102 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h @@ -131,14 +131,12 @@ static int hdcs_probe_1x00(struct sd *sd); static int hdcs_probe_1020(struct sd *sd); static int hdcs_start(struct sd *sd); static int hdcs_init(struct sd *sd); +static int hdcs_init_controls(struct sd *sd); static int hdcs_stop(struct sd *sd); static int hdcs_dump(struct sd *sd); -static void hdcs_disconnect(struct sd *sd); -static int hdcs_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int hdcs_set_exposure(struct gspca_dev *gspca_dev, __s32 val); static int hdcs_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int hdcs_get_gain(struct gspca_dev *gspca_dev, __s32 *val); const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { .name = "HP HDCS-1000/1100", @@ -152,10 +150,10 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = { .max_packet_size = { 847 }, .init = hdcs_init, + .init_controls = hdcs_init_controls, .probe = hdcs_probe_1x00, .start = hdcs_start, .stop = hdcs_stop, - .disconnect = hdcs_disconnect, .dump = hdcs_dump, }; @@ -171,6 +169,7 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = { .max_packet_size = { 847 }, .init = hdcs_init, + .init_controls = hdcs_init_controls, .probe = hdcs_probe_1020, .start = hdcs_start, .stop = hdcs_stop, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c index 26f14fc4a135..cdfc3d05ab6b 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c @@ -48,105 +48,16 @@ #include "stv06xx_pb0100.h" -static const struct ctrl pb0100_ctrl[] = { -#define GAIN_IDX 0 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128 - }, - .set = pb0100_set_gain, - .get = pb0100_get_gain - }, -#define RED_BALANCE_IDX 1 - { - { - .id = V4L2_CID_RED_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Red Balance", - .minimum = -255, - .maximum = 255, - .step = 1, - .default_value = 0 - }, - .set = pb0100_set_red_balance, - .get = pb0100_get_red_balance - }, -#define BLUE_BALANCE_IDX 2 - { - { - .id = V4L2_CID_BLUE_BALANCE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Blue Balance", - .minimum = -255, - .maximum = 255, - .step = 1, - .default_value = 0 - }, - .set = pb0100_set_blue_balance, - .get = pb0100_get_blue_balance - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, - .maximum = 511, - .step = 1, - .default_value = 12 - }, - .set = pb0100_set_exposure, - .get = pb0100_get_exposure - }, -#define AUTOGAIN_IDX 4 - { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Automatic Gain and Exposure", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = pb0100_set_autogain, - .get = pb0100_get_autogain - }, -#define AUTOGAIN_TARGET_IDX 5 - { - { - .id = V4L2_CTRL_CLASS_USER + 0x1000, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Automatic Gain Target", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128 - }, - .set = pb0100_set_autogain_target, - .get = pb0100_get_autogain_target - }, -#define NATURAL_IDX 6 - { - { - .id = V4L2_CTRL_CLASS_USER + 0x1001, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Natural Light Source", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1 - }, - .set = pb0100_set_natural, - .get = pb0100_get_natural - } +struct pb0100_ctrls { + struct { /* one big happy control cluster... */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *red; + struct v4l2_ctrl *blue; + struct v4l2_ctrl *natural; + }; + struct v4l2_ctrl *target; }; static struct v4l2_pix_format pb0100_mode[] = { @@ -174,38 +85,104 @@ static struct v4l2_pix_format pb0100_mode[] = { } }; +static int pb0100_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + struct pb0100_ctrls *ctrls = sd->sensor_priv; + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + err = pb0100_set_autogain(gspca_dev, ctrl->val); + if (err) + break; + if (ctrl->val) + break; + err = pb0100_set_gain(gspca_dev, ctrls->gain->val); + if (err) + break; + err = pb0100_set_exposure(gspca_dev, ctrls->exposure->val); + break; + case V4L2_CTRL_CLASS_USER + 0x1001: + err = pb0100_set_autogain_target(gspca_dev, ctrl->val); + break; + } + return err; +} + +static const struct v4l2_ctrl_ops pb0100_ctrl_ops = { + .s_ctrl = pb0100_s_ctrl, +}; + +static int pb0100_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + struct pb0100_ctrls *ctrls; + static const struct v4l2_ctrl_config autogain_target = { + .ops = &pb0100_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER + 0x1000, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Automatic Gain Target", + .max = 255, + .step = 1, + .def = 128, + }; + static const struct v4l2_ctrl_config natural_light = { + .ops = &pb0100_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER + 0x1001, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Natural Light Source", + .max = 1, + .step = 1, + .def = 1, + }; + + ctrls = kzalloc(sizeof(*ctrls), GFP_KERNEL); + if (!ctrls) + return -ENOMEM; + + v4l2_ctrl_handler_init(hdl, 6); + ctrls->autogain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + ctrls->exposure = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 511, 1, 12); + ctrls->gain = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 128); + ctrls->red = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_RED_BALANCE, -255, 255, 1, 0); + ctrls->blue = v4l2_ctrl_new_std(hdl, &pb0100_ctrl_ops, + V4L2_CID_BLUE_BALANCE, -255, 255, 1, 0); + ctrls->natural = v4l2_ctrl_new_custom(hdl, &natural_light, NULL); + ctrls->target = v4l2_ctrl_new_custom(hdl, &autogain_target, NULL); + if (hdl->error) { + kfree(ctrls); + return hdl->error; + } + sd->sensor_priv = ctrls; + v4l2_ctrl_auto_cluster(5, &ctrls->autogain, 0, false); + return 0; +} + static int pb0100_probe(struct sd *sd) { u16 sensor; - int i, err; - s32 *sensor_settings; + int err; err = stv06xx_read_sensor(sd, PB_IDENT, &sensor); if (err < 0) return -ENODEV; + if ((sensor >> 8) != 0x64) + return -ENODEV; - if ((sensor >> 8) == 0x64) { - sensor_settings = kmalloc( - ARRAY_SIZE(pb0100_ctrl) * sizeof(s32), - GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; + pr_info("Photobit pb0100 sensor detected\n"); - pr_info("Photobit pb0100 sensor detected\n"); + sd->gspca_dev.cam.cam_mode = pb0100_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); - sd->gspca_dev.cam.cam_mode = pb0100_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode); - sd->desc.ctrls = pb0100_ctrl; - sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl); - for (i = 0; i < sd->desc.nctrls; i++) - sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - - return 0; - } - - return -ENODEV; + return 0; } static int pb0100_start(struct sd *sd) @@ -214,7 +191,6 @@ static int pb0100_start(struct sd *sd) struct usb_host_interface *alt; struct usb_interface *intf; struct cam *cam = &sd->gspca_dev.cam; - s32 *sensor_settings = sd->sensor_priv; u32 mode = cam->cam_mode[sd->gspca_dev.curr_mode].priv; intf = usb_ifnum_to_if(sd->gspca_dev.dev, sd->gspca_dev.iface); @@ -255,13 +231,6 @@ static int pb0100_start(struct sd *sd) stv06xx_write_bridge(sd, STV_SCAN_RATE, 0x20); } - /* set_gain also sets red and blue balance */ - pb0100_set_gain(&sd->gspca_dev, sensor_settings[GAIN_IDX]); - pb0100_set_exposure(&sd->gspca_dev, sensor_settings[EXPOSURE_IDX]); - pb0100_set_autogain_target(&sd->gspca_dev, - sensor_settings[AUTOGAIN_TARGET_IDX]); - pb0100_set_autogain(&sd->gspca_dev, sensor_settings[AUTOGAIN_IDX]); - err = stv06xx_write_sensor(sd, PB_CONTROL, BIT(5)|BIT(3)|BIT(1)); PDEBUG(D_STREAM, "Started stream, status: %d", err); @@ -285,12 +254,6 @@ out: return (err < 0) ? err : 0; } -static void pb0100_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - /* FIXME: Sort the init commands out and put them into tables, this is only for getting the camera to work */ /* FIXME: No error handling for now, @@ -362,62 +325,32 @@ static int pb0100_dump(struct sd *sd) return 0; } -static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - - return 0; -} - static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[GAIN_IDX] = val; err = stv06xx_write_sensor(sd, PB_G1GAIN, val); if (!err) err = stv06xx_write_sensor(sd, PB_G2GAIN, val); PDEBUG(D_V4L2, "Set green gain to %d, status: %d", val, err); if (!err) - err = pb0100_set_red_balance(gspca_dev, - sensor_settings[RED_BALANCE_IDX]); + err = pb0100_set_red_balance(gspca_dev, ctrls->red->val); if (!err) - err = pb0100_set_blue_balance(gspca_dev, - sensor_settings[BLUE_BALANCE_IDX]); + err = pb0100_set_blue_balance(gspca_dev, ctrls->blue->val); return err; } -static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[RED_BALANCE_IDX]; - - return 0; -} - static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[RED_BALANCE_IDX] = val; - val += sensor_settings[GAIN_IDX]; + val += ctrls->gain->val; if (val < 0) val = 0; else if (val > 255) @@ -429,27 +362,13 @@ static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[BLUE_BALANCE_IDX]; - - return 0; -} - static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[BLUE_BALANCE_IDX] = val; - val += sensor_settings[GAIN_IDX]; + val += ctrls->gain->val; if (val < 0) val = 0; else if (val > 255) @@ -461,51 +380,25 @@ static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val) return err; } -static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - - return 0; -} - static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { - int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + int err; - if (sensor_settings[AUTOGAIN_IDX]) - return -EBUSY; - - sensor_settings[EXPOSURE_IDX] = val; err = stv06xx_write_sensor(sd, PB_RINTTIME, val); PDEBUG(D_V4L2, "Set exposure to %d, status: %d", val, err); return err; } -static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTOGAIN_IDX]; - - return 0; -} - static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; + struct pb0100_ctrls *ctrls = sd->sensor_priv; - sensor_settings[AUTOGAIN_IDX] = val; - if (sensor_settings[AUTOGAIN_IDX]) { - if (sensor_settings[NATURAL_IDX]) + if (val) { + if (ctrls->natural->val) val = BIT(6)|BIT(4)|BIT(0); else val = BIT(4)|BIT(0); @@ -514,29 +407,15 @@ static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val) err = stv06xx_write_sensor(sd, PB_EXPGAIN, val); PDEBUG(D_V4L2, "Set autogain to %d (natural: %d), status: %d", - sensor_settings[AUTOGAIN_IDX], sensor_settings[NATURAL_IDX], - err); + val, ctrls->natural->val, err); return err; } -static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[AUTOGAIN_TARGET_IDX]; - - return 0; -} - static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) { int err, totalpixels, brightpixels, darkpixels; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[AUTOGAIN_TARGET_IDX] = val; /* Number of pixels counted by the sensor when subsampling the pixels. * Slightly larger than the real value to avoid oscillation */ @@ -553,23 +432,3 @@ static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val) return err; } - -static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[NATURAL_IDX]; - - return 0; -} - -static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - sensor_settings[NATURAL_IDX] = val; - - return pb0100_set_autogain(gspca_dev, sensor_settings[AUTOGAIN_IDX]); -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h index 757de246dc75..5071e5353fd3 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h @@ -112,25 +112,17 @@ static int pb0100_probe(struct sd *sd); static int pb0100_start(struct sd *sd); static int pb0100_init(struct sd *sd); +static int pb0100_init_controls(struct sd *sd); static int pb0100_stop(struct sd *sd); static int pb0100_dump(struct sd *sd); -static void pb0100_disconnect(struct sd *sd); /* V4L2 controls supported by the driver */ -static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_gain(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_red_balance(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_red_balance(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_blue_balance(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_blue_balance(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_exposure(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_autogain(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_autogain(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_autogain_target(struct gspca_dev *gspca_dev, __s32 *val); static int pb0100_set_autogain_target(struct gspca_dev *gspca_dev, __s32 val); -static int pb0100_get_natural(struct gspca_dev *gspca_dev, __s32 *val); -static int pb0100_set_natural(struct gspca_dev *gspca_dev, __s32 val); const struct stv06xx_sensor stv06xx_sensor_pb0100 = { .name = "PB-0100", @@ -142,11 +134,11 @@ const struct stv06xx_sensor stv06xx_sensor_pb0100 = { .max_packet_size = { 847, 923 }, .init = pb0100_init, + .init_controls = pb0100_init_controls, .probe = pb0100_probe, .start = pb0100_start, .stop = pb0100_stop, .dump = pb0100_dump, - .disconnect = pb0100_disconnect, }; #endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h index fb229d8ded58..3a498c2495c6 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h @@ -63,8 +63,8 @@ struct stv06xx_sensor { /* Performs a initialization sequence */ int (*init)(struct sd *sd); - /* Executed at device disconnect */ - void (*disconnect)(struct sd *sd); + /* Initializes the controls */ + int (*init_controls)(struct sd *sd); /* Reads a sensor register */ int (*read_sensor)(struct sd *sd, const u8 address, diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c index 9940e035b3ab..8a57990dfe0f 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.c @@ -30,20 +30,6 @@ #include "stv06xx_st6422.h" -/* controls */ -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - GAIN, - EXPOSURE, - NCTRLS /* number of controls */ -}; - -/* sensor settings */ -struct st6422_settings { - struct gspca_ctrl ctrls[NCTRLS]; -}; - static struct v4l2_pix_format st6422_mode[] = { /* Note we actually get 124 lines of data, of which we skip the 4st 4 as they are garbage */ @@ -74,83 +60,70 @@ static struct v4l2_pix_format st6422_mode[] = { }; /* V4L2 controls supported by the driver */ -static void st6422_set_brightness(struct gspca_dev *gspca_dev); -static void st6422_set_contrast(struct gspca_dev *gspca_dev); -static void st6422_set_gain(struct gspca_dev *gspca_dev); -static void st6422_set_exposure(struct gspca_dev *gspca_dev); +static int setbrightness(struct sd *sd, s32 val); +static int setcontrast(struct sd *sd, s32 val); +static int setgain(struct sd *sd, u8 gain); +static int setexposure(struct sd *sd, s16 expo); -static const struct ctrl st6422_ctrl[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 31, - .step = 1, - .default_value = 3 - }, - .set_control = st6422_set_brightness - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 11 - }, - .set_control = st6422_set_contrast - }, -[GAIN] = { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gain", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 64 - }, - .set_control = st6422_set_gain - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0, -#define EXPOSURE_MAX 1023 - .maximum = EXPOSURE_MAX, - .step = 1, - .default_value = 256 - }, - .set_control = st6422_set_exposure - }, +static int st6422_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + err = setbrightness(sd, ctrl->val); + break; + case V4L2_CID_CONTRAST: + err = setcontrast(sd, ctrl->val); + break; + case V4L2_CID_GAIN: + err = setgain(sd, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = setexposure(sd, ctrl->val); + break; + } + + /* commit settings */ + if (err >= 0) + err = stv06xx_write_bridge(sd, 0x143f, 0x01); + sd->gspca_dev.usb_err = err; + return err; +} + +static const struct v4l2_ctrl_ops st6422_ctrl_ops = { + .s_ctrl = st6422_s_ctrl, }; +static int st6422_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 31, 1, 3); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_CONTRAST, 0, 15, 1, 11); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 1023, 1, 256); + v4l2_ctrl_new_std(hdl, &st6422_ctrl_ops, + V4L2_CID_GAIN, 0, 255, 1, 64); + + return hdl->error; +} + static int st6422_probe(struct sd *sd) { - struct st6422_settings *sensor_settings; - if (sd->bridge != BRIDGE_ST6422) return -ENODEV; pr_info("st6422 sensor detected\n"); - sensor_settings = kmalloc(sizeof *sensor_settings, GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; - sd->gspca_dev.cam.cam_mode = st6422_mode; sd->gspca_dev.cam.nmodes = ARRAY_SIZE(st6422_mode); - sd->gspca_dev.cam.ctrls = sensor_settings->ctrls; - sd->desc.ctrls = st6422_ctrl; - sd->desc.nctrls = ARRAY_SIZE(st6422_ctrl); - sd->sensor_priv = sensor_settings; - return 0; } @@ -239,38 +212,22 @@ static int st6422_init(struct sd *sd) return err; } -static void st6422_disconnect(struct sd *sd) +static int setbrightness(struct sd *sd, s32 val) { - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - -static int setbrightness(struct sd *sd) -{ - struct st6422_settings *sensor_settings = sd->sensor_priv; - /* val goes from 0 -> 31 */ - return stv06xx_write_bridge(sd, 0x1432, - sensor_settings->ctrls[BRIGHTNESS].val); + return stv06xx_write_bridge(sd, 0x1432, val); } -static int setcontrast(struct sd *sd) +static int setcontrast(struct sd *sd, s32 val) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - /* Val goes from 0 -> 15 */ - return stv06xx_write_bridge(sd, 0x143a, - sensor_settings->ctrls[CONTRAST].val | 0xf0); + return stv06xx_write_bridge(sd, 0x143a, val | 0xf0); } -static int setgain(struct sd *sd) +static int setgain(struct sd *sd, u8 gain) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - u8 gain; int err; - gain = sensor_settings->ctrls[GAIN].val; - /* Set red, green, blue, gain */ err = stv06xx_write_bridge(sd, 0x0509, gain); if (err < 0) @@ -292,13 +249,10 @@ static int setgain(struct sd *sd) return stv06xx_write_bridge(sd, 0x050d, 0x01); } -static int setexposure(struct sd *sd) +static int setexposure(struct sd *sd, s16 expo) { - struct st6422_settings *sensor_settings = sd->sensor_priv; - u16 expo; int err; - expo = sensor_settings->ctrls[EXPOSURE].val; err = stv06xx_write_bridge(sd, 0x143d, expo & 0xff); if (err < 0) return err; @@ -318,22 +272,6 @@ static int st6422_start(struct sd *sd) if (err < 0) return err; - err = setbrightness(sd); - if (err < 0) - return err; - - err = setcontrast(sd); - if (err < 0) - return err; - - err = setexposure(sd); - if (err < 0) - return err; - - err = setgain(sd); - if (err < 0) - return err; - /* commit settings */ err = stv06xx_write_bridge(sd, 0x143f, 0x01); return (err < 0) ? err : 0; @@ -345,59 +283,3 @@ static int st6422_stop(struct sd *sd) return 0; } - -static void st6422_set_brightness(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setbrightness(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_contrast(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setcontrast(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_gain(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setgain(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} - -static void st6422_set_exposure(struct gspca_dev *gspca_dev) -{ - int err; - struct sd *sd = (struct sd *) gspca_dev; - - err = setexposure(sd); - - /* commit settings */ - if (err >= 0) - err = stv06xx_write_bridge(sd, 0x143f, 0x01); - - gspca_dev->usb_err = err; -} diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h index d7498e06432b..8f20fbf30f33 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_st6422.h @@ -34,8 +34,8 @@ static int st6422_probe(struct sd *sd); static int st6422_start(struct sd *sd); static int st6422_init(struct sd *sd); +static int st6422_init_controls(struct sd *sd); static int st6422_stop(struct sd *sd); -static void st6422_disconnect(struct sd *sd); const struct stv06xx_sensor stv06xx_sensor_st6422 = { .name = "ST6422", @@ -43,10 +43,10 @@ const struct stv06xx_sensor stv06xx_sensor_st6422 = { .min_packet_size = { 300, 847 }, .max_packet_size = { 300, 847 }, .init = st6422_init, + .init_controls = st6422_init_controls, .probe = st6422_probe, .start = st6422_start, .stop = st6422_stop, - .disconnect = st6422_disconnect, }; #endif diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c index a5c69d9ebdd4..748e1421d6d8 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c @@ -44,130 +44,83 @@ static struct v4l2_pix_format vv6410_mode[] = { } }; -static const struct ctrl vv6410_ctrl[] = { -#define HFLIP_IDX 0 - { - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "horizontal flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_hflip, - .get = vv6410_get_hflip - }, -#define VFLIP_IDX 1 - { - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "vertical flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 0 - }, - .set = vv6410_set_vflip, - .get = vv6410_get_vflip - }, -#define GAIN_IDX 2 - { - { - .id = V4L2_CID_GAIN, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "analog gain", - .minimum = 0, - .maximum = 15, - .step = 1, - .default_value = 10 - }, - .set = vv6410_set_analog_gain, - .get = vv6410_get_analog_gain - }, -#define EXPOSURE_IDX 3 - { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "exposure", - .minimum = 0, - .maximum = 32768, - .step = 1, - .default_value = 20000 - }, - .set = vv6410_set_exposure, - .get = vv6410_get_exposure +static int vv6410_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + int err = -EINVAL; + + switch (ctrl->id) { + case V4L2_CID_HFLIP: + err = vv6410_set_hflip(gspca_dev, ctrl->val); + break; + case V4L2_CID_VFLIP: + err = vv6410_set_vflip(gspca_dev, ctrl->val); + break; + case V4L2_CID_GAIN: + err = vv6410_set_analog_gain(gspca_dev, ctrl->val); + break; + case V4L2_CID_EXPOSURE: + err = vv6410_set_exposure(gspca_dev, ctrl->val); + break; } - }; + return err; +} + +static const struct v4l2_ctrl_ops vv6410_ctrl_ops = { + .s_ctrl = vv6410_s_ctrl, +}; static int vv6410_probe(struct sd *sd) { u16 data; - int err, i; - s32 *sensor_settings; + int err; err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data); if (err < 0) return -ENODEV; - if (data == 0x19) { - pr_info("vv6410 sensor detected\n"); + if (data != 0x19) + return -ENODEV; - sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32), - GFP_KERNEL); - if (!sensor_settings) - return -ENOMEM; + pr_info("vv6410 sensor detected\n"); - sd->gspca_dev.cam.cam_mode = vv6410_mode; - sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); - sd->desc.ctrls = vv6410_ctrl; - sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl); + sd->gspca_dev.cam.cam_mode = vv6410_mode; + sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode); + return 0; +} - for (i = 0; i < sd->desc.nctrls; i++) - sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value; - sd->sensor_priv = sensor_settings; - return 0; - } - return -ENODEV; +static int vv6410_init_controls(struct sd *sd) +{ + struct v4l2_ctrl_handler *hdl = &sd->gspca_dev.ctrl_handler; + + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 32768, 1, 20000); + v4l2_ctrl_new_std(hdl, &vv6410_ctrl_ops, + V4L2_CID_GAIN, 0, 15, 1, 10); + return hdl->error; } static int vv6410_init(struct sd *sd) { int err = 0, i; - s32 *sensor_settings = sd->sensor_priv; - for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) { + for (i = 0; i < ARRAY_SIZE(stv_bridge_init); i++) stv06xx_write_bridge(sd, stv_bridge_init[i].addr, stv_bridge_init[i].data); - } if (err < 0) return err; err = stv06xx_write_sensor_bytes(sd, (u8 *) vv6410_sensor_init, ARRAY_SIZE(vv6410_sensor_init)); - if (err < 0) - return err; - - err = vv6410_set_exposure(&sd->gspca_dev, - sensor_settings[EXPOSURE_IDX]); - if (err < 0) - return err; - - err = vv6410_set_analog_gain(&sd->gspca_dev, - sensor_settings[GAIN_IDX]); - return (err < 0) ? err : 0; } -static void vv6410_disconnect(struct sd *sd) -{ - sd->sensor = NULL; - kfree(sd->sensor_priv); -} - static int vv6410_start(struct sd *sd) { int err; @@ -233,25 +186,12 @@ static int vv6410_dump(struct sd *sd) return (err < 0) ? err : 0; } -static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[HFLIP_IDX]; - PDEBUG(D_V4L2, "Read horizontal flip %d", *val); - - return 0; -} - static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[HFLIP_IDX] = val; err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); if (err < 0) return err; @@ -267,25 +207,12 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val) return (err < 0) ? err : 0; } -static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[VFLIP_IDX]; - PDEBUG(D_V4L2, "Read vertical flip %d", *val); - - return 0; -} - static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) { int err; u16 i2c_data; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[VFLIP_IDX] = val; err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data); if (err < 0) return err; @@ -301,52 +228,23 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val) return (err < 0) ? err : 0; } -static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[GAIN_IDX]; - - PDEBUG(D_V4L2, "Read analog gain %d", *val); - - return 0; -} - static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - sensor_settings[GAIN_IDX] = val; PDEBUG(D_V4L2, "Set analog gain to %d", val); err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf)); return (err < 0) ? err : 0; } -static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val) -{ - struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; - - *val = sensor_settings[EXPOSURE_IDX]; - - PDEBUG(D_V4L2, "Read exposure %d", *val); - - return 0; -} - static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val) { int err; struct sd *sd = (struct sd *) gspca_dev; - s32 *sensor_settings = sd->sensor_priv; unsigned int fine, coarse; - sensor_settings[EXPOSURE_IDX] = val; - val = (val * val >> 14) + val / 4; fine = val % VV6410_CIF_LINELENGTH; diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h index a25b8873f2e6..53e67b40ca05 100644 --- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h +++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h @@ -178,18 +178,14 @@ static int vv6410_probe(struct sd *sd); static int vv6410_start(struct sd *sd); static int vv6410_init(struct sd *sd); +static int vv6410_init_controls(struct sd *sd); static int vv6410_stop(struct sd *sd); static int vv6410_dump(struct sd *sd); -static void vv6410_disconnect(struct sd *sd); /* V4L2 controls supported by the driver */ -static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val); -static int vv6410_get_exposure(struct gspca_dev *gspca_dev, __s32 *val); static int vv6410_set_exposure(struct gspca_dev *gspca_dev, __s32 val); const struct stv06xx_sensor stv06xx_sensor_vv6410 = { @@ -202,11 +198,11 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = { .min_packet_size = { 1023 }, .max_packet_size = { 1023 }, .init = vv6410_init, + .init_controls = vv6410_init_controls, .probe = vv6410_probe, .start = vv6410_start, .stop = vv6410_stop, .dump = vv6410_dump, - .disconnect = vv6410_disconnect, }; /* If NULL, only single value to write, stored in len */ diff --git a/drivers/media/video/gspca/topro.c b/drivers/media/video/gspca/topro.c index 444d3c5b9079..c6326d177a3d 100644 --- a/drivers/media/video/gspca/topro.c +++ b/drivers/media/video/gspca/topro.c @@ -4675,11 +4675,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, /* -- do autogain -- */ /* gain setting is done in setexposure() for tp6810 */ static void setgain(struct gspca_dev *gspca_dev) {} -/* !! coarse_grained_expo_autogain is not used !! */ -#define exp_too_low_cnt bridge -#define exp_too_high_cnt sensor - +#define WANT_REGULAR_AUTOGAIN #include "autogain_functions.h" + static void sd_dq_callback(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; diff --git a/drivers/media/video/gspca/vicam.c b/drivers/media/video/gspca/vicam.c index 911152e169d6..15a30f7a4b2a 100644 --- a/drivers/media/video/gspca/vicam.c +++ b/drivers/media/video/gspca/vicam.c @@ -37,9 +37,12 @@ #include #include "gspca.h" +#define VICAM_FIRMWARE "vicam/firmware.fw" + MODULE_AUTHOR("Hans de Goede "); MODULE_DESCRIPTION("GSPCA ViCam USB Camera Driver"); MODULE_LICENSE("GPL"); +MODULE_FIRMWARE(VICAM_FIRMWARE); enum e_ctrl { GAIN, @@ -222,7 +225,11 @@ static void vicam_dostream(struct work_struct *work) goto exit; } - while (gspca_dev->present && gspca_dev->streaming) { + while (gspca_dev->dev && gspca_dev->streaming) { +#ifdef CONFIG_PM + if (gspca_dev->frozen) + break; +#endif ret = vicam_read_frame(gspca_dev, buffer, frame_sz); if (ret < 0) break; @@ -268,7 +275,7 @@ static int sd_init(struct gspca_dev *gspca_dev) const struct firmware *uninitialized_var(fw); u8 *firmware_buf; - ret = request_ihex_firmware(&fw, "vicam/firmware.fw", + ret = request_ihex_firmware(&fw, VICAM_FIRMWARE, &gspca_dev->dev->dev); if (ret) { pr_err("Failed to load \"vicam/firmware.fw\": %d\n", ret); @@ -324,7 +331,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) dev->work_thread = NULL; mutex_lock(&gspca_dev->usb_lock); - if (gspca_dev->present) + if (gspca_dev->dev) vicam_set_camera_power(gspca_dev, 0); } diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index 7d9a4f1be9dc..f0bacee33ef9 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c @@ -32,29 +32,25 @@ MODULE_LICENSE("GPL"); static int force_sensor = -1; -#define REG08_DEF 3 /* default JPEG compression (70%) */ +#define REG08_DEF 3 /* default JPEG compression (75%) */ #include "zc3xx-reg.h" -/* controls */ -enum e_ctrl { - BRIGHTNESS, - CONTRAST, - EXPOSURE, - GAMMA, - AUTOGAIN, - LIGHTFREQ, - SHARPNESS, - QUALITY, - NCTRLS /* number of controls */ -}; - -#define AUTOGAIN_DEF 1 - /* specific webcam descriptor */ struct sd { struct gspca_dev gspca_dev; /* !! must be the first item */ - struct gspca_ctrl ctrls[NCTRLS]; + struct { /* gamma/brightness/contrast control cluster */ + struct v4l2_ctrl *gamma; + struct v4l2_ctrl *brightness; + struct v4l2_ctrl *contrast; + }; + struct { /* autogain/exposure control cluster */ + struct v4l2_ctrl *autogain; + struct v4l2_ctrl *exposure; + }; + struct v4l2_ctrl *plfreq; + struct v4l2_ctrl *sharpness; + struct v4l2_ctrl *jpegqual; struct work_struct work; struct workqueue_struct *work_thread; @@ -94,114 +90,6 @@ enum sensors { SENSOR_MAX }; -/* V4L2 controls supported by the driver */ -static void setcontrast(struct gspca_dev *gspca_dev); -static void setexposure(struct gspca_dev *gspca_dev); -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val); -static void setlightfreq(struct gspca_dev *gspca_dev); -static void setsharpness(struct gspca_dev *gspca_dev); -static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val); - -static const struct ctrl sd_ctrls[NCTRLS] = { -[BRIGHTNESS] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - }, - .set_control = setcontrast - }, -[CONTRAST] = { - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = 128, - }, - .set_control = setcontrast - }, -[EXPOSURE] = { - { - .id = V4L2_CID_EXPOSURE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Exposure", - .minimum = 0x30d, - .maximum = 0x493e, - .step = 1, - .default_value = 0x927 - }, - .set_control = setexposure - }, -[GAMMA] = { - { - .id = V4L2_CID_GAMMA, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Gamma", - .minimum = 1, - .maximum = 6, - .step = 1, - .default_value = 4, - }, - .set_control = setcontrast - }, -[AUTOGAIN] = { - { - .id = V4L2_CID_AUTOGAIN, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Auto Gain", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = AUTOGAIN_DEF, - .flags = V4L2_CTRL_FLAG_UPDATE - }, - .set = sd_setautogain - }, -[LIGHTFREQ] = { - { - .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, - .default_value = 0, - }, - .set_control = setlightfreq - }, -[SHARPNESS] = { - { - .id = V4L2_CID_SHARPNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Sharpness", - .minimum = 0, - .maximum = 3, - .step = 1, - .default_value = 2, - }, - .set_control = setsharpness - }, -[QUALITY] = { - { - .id = V4L2_CID_JPEG_COMPRESSION_QUALITY, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Compression Quality", - .minimum = 40, - .maximum = 70, - .step = 1, - .default_value = 70 /* updated in sd_init() */ - }, - .set = sd_setquality - }, -}; - static const struct v4l2_pix_format vga_mode[] = { {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, .bytesperline = 320, @@ -241,8 +129,11 @@ static const struct v4l2_pix_format sif_mode[] = { .priv = 0}, }; -/* bridge reg08 -> JPEG quality conversion table */ -static u8 jpeg_qual[] = {40, 50, 60, 70, /*80*/}; +/* + * Bridge reg08 bits 1-2 -> JPEG quality conversion table. Note the highest + * quality setting is not usable as USB 1 does not have enough bandwidth. + */ +static u8 jpeg_qual[] = {50, 75, 87, /* 94 */}; /* usb exchanges */ struct usb_action { @@ -5818,10 +5709,8 @@ static void setmatrix(struct gspca_dev *gspca_dev) reg_w(gspca_dev, matrix[i], 0x010a + i); } -static void setsharpness(struct gspca_dev *gspca_dev) +static void setsharpness(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - int sharpness; static const u8 sharpness_tb[][2] = { {0x02, 0x03}, {0x04, 0x07}, @@ -5829,19 +5718,18 @@ static void setsharpness(struct gspca_dev *gspca_dev) {0x10, 0x1e} }; - sharpness = sd->ctrls[SHARPNESS].val; - reg_w(gspca_dev, sharpness_tb[sharpness][0], 0x01c6); + reg_w(gspca_dev, sharpness_tb[val][0], 0x01c6); reg_r(gspca_dev, 0x01c8); reg_r(gspca_dev, 0x01c9); reg_r(gspca_dev, 0x01ca); - reg_w(gspca_dev, sharpness_tb[sharpness][1], 0x01cb); + reg_w(gspca_dev, sharpness_tb[val][1], 0x01cb); } -static void setcontrast(struct gspca_dev *gspca_dev) +static void setcontrast(struct gspca_dev *gspca_dev, + s32 gamma, s32 brightness, s32 contrast) { - struct sd *sd = (struct sd *) gspca_dev; const u8 *Tgamma; - int g, i, brightness, contrast, adj, gp1, gp2; + int g, i, adj, gp1, gp2; u8 gr[16]; static const u8 delta_b[16] = /* delta for brightness */ {0x50, 0x38, 0x2d, 0x28, 0x24, 0x21, 0x1e, 0x1d, @@ -5864,10 +5752,10 @@ static void setcontrast(struct gspca_dev *gspca_dev) 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff}, }; - Tgamma = gamma_tb[sd->ctrls[GAMMA].val - 1]; + Tgamma = gamma_tb[gamma - 1]; - contrast = ((int) sd->ctrls[CONTRAST].val - 128); /* -128 / 127 */ - brightness = ((int) sd->ctrls[BRIGHTNESS].val - 128); /* -128 / 92 */ + contrast -= 128; /* -128 / 127 */ + brightness -= 128; /* -128 / 92 */ adj = 0; gp1 = gp2 = 0; for (i = 0; i < 16; i++) { @@ -5894,25 +5782,15 @@ static void setcontrast(struct gspca_dev *gspca_dev) reg_w(gspca_dev, gr[i], 0x0130 + i); /* gradient */ } -static void getexposure(struct gspca_dev *gspca_dev) +static s32 getexposure(struct gspca_dev *gspca_dev) { - struct sd *sd = (struct sd *) gspca_dev; - - if (sd->sensor != SENSOR_HV7131R) - return; - sd->ctrls[EXPOSURE].val = (i2c_read(gspca_dev, 0x25) << 9) + return (i2c_read(gspca_dev, 0x25) << 9) | (i2c_read(gspca_dev, 0x26) << 1) | (i2c_read(gspca_dev, 0x27) >> 7); } -static void setexposure(struct gspca_dev *gspca_dev) +static void setexposure(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - int val; - - if (sd->sensor != SENSOR_HV7131R) - return; - val = sd->ctrls[EXPOSURE].val; i2c_write(gspca_dev, 0x25, val >> 9, 0x00); i2c_write(gspca_dev, 0x26, val >> 1, 0x00); i2c_write(gspca_dev, 0x27, val << 7, 0x00); @@ -5921,20 +5799,8 @@ static void setexposure(struct gspca_dev *gspca_dev) static void setquality(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; - s8 reg07; - - reg07 = 0; - switch (sd->sensor) { - case SENSOR_OV7620: - reg07 = 0x30; - break; - case SENSOR_HV7131R: - case SENSOR_PAS202B: - return; /* done by work queue */ - } + jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08 >> 1]); reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); - if (reg07 != 0) - reg_w(gspca_dev, reg07, 0x0007); } /* Matches the sensor's internal frame rate to the lighting frequency. @@ -5943,7 +5809,7 @@ static void setquality(struct gspca_dev *gspca_dev) * 60Hz, for American lighting * 0 = No Fliker (for outdoore usage) */ -static void setlightfreq(struct gspca_dev *gspca_dev) +static void setlightfreq(struct gspca_dev *gspca_dev, s32 val) { struct sd *sd = (struct sd *) gspca_dev; int i, mode; @@ -6027,7 +5893,7 @@ static void setlightfreq(struct gspca_dev *gspca_dev) tas5130c_60HZ, tas5130c_60HZScale}, }; - i = sd->ctrls[LIGHTFREQ].val * 2; + i = val * 2; mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv; if (mode) i++; /* 320x240 */ @@ -6037,14 +5903,14 @@ static void setlightfreq(struct gspca_dev *gspca_dev) usb_exchange(gspca_dev, zc3_freq); switch (sd->sensor) { case SENSOR_GC0305: - if (mode /* if 320x240 */ - && sd->ctrls[LIGHTFREQ].val == 1) /* and 50Hz */ + if (mode /* if 320x240 */ + && val == 1) /* and 50Hz */ reg_w(gspca_dev, 0x85, 0x018d); /* win: 0x80, 0x018d */ break; case SENSOR_OV7620: - if (!mode) { /* if 640x480 */ - if (sd->ctrls[LIGHTFREQ].val != 0) /* and filter */ + if (!mode) { /* if 640x480 */ + if (val != 0) /* and filter */ reg_w(gspca_dev, 0x40, 0x0002); else reg_w(gspca_dev, 0x44, 0x0002); @@ -6056,22 +5922,15 @@ static void setlightfreq(struct gspca_dev *gspca_dev) } } -static void setautogain(struct gspca_dev *gspca_dev) +static void setautogain(struct gspca_dev *gspca_dev, s32 val) { - struct sd *sd = (struct sd *) gspca_dev; - u8 autoval; - - if (sd->ctrls[AUTOGAIN].val) - autoval = 0x42; - else - autoval = 0x02; - reg_w(gspca_dev, autoval, 0x0180); + reg_w(gspca_dev, val ? 0x42 : 0x02, 0x0180); } -/* update the transfer parameters */ -/* This function is executed from a work queue. */ -/* The exact use of the bridge registers 07 and 08 is not known. - * The following algorithm has been adapted from ms-win traces */ +/* + * Update the transfer parameters. + * This function is executed from a work queue. + */ static void transfer_update(struct work_struct *work) { struct sd *sd = container_of(work, struct sd, work); @@ -6079,96 +5938,55 @@ static void transfer_update(struct work_struct *work) int change, good; u8 reg07, reg11; - /* synchronize with the main driver and initialize the registers */ - mutex_lock(&gspca_dev->usb_lock); - reg07 = 0; /* max */ - reg_w(gspca_dev, reg07, 0x0007); - reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); - mutex_unlock(&gspca_dev->usb_lock); + /* reg07 gets set to 0 by sd_start before starting us */ + reg07 = 0; good = 0; for (;;) { msleep(100); - /* get the transfer status */ - /* the bit 0 of the bridge register 11 indicates overflow */ mutex_lock(&gspca_dev->usb_lock); - if (!gspca_dev->present || !gspca_dev->streaming) +#ifdef CONFIG_PM + if (gspca_dev->frozen) goto err; +#endif + if (!gspca_dev->dev || !gspca_dev->streaming) + goto err; + + /* Bit 0 of register 11 indicates FIFO overflow */ + gspca_dev->usb_err = 0; reg11 = reg_r(gspca_dev, 0x0011); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present || !gspca_dev->streaming) + if (gspca_dev->usb_err) goto err; change = reg11 & 0x01; if (change) { /* overflow */ - switch (reg07) { - case 0: /* max */ - reg07 = sd->sensor == SENSOR_HV7131R - ? 0x30 : 0x32; - if (sd->reg08 != 0) { - change = 3; - sd->reg08--; - } - break; - case 0x32: - reg07 -= 4; - break; - default: - reg07 -= 2; - break; - case 2: - change = 0; /* already min */ - break; - } good = 0; + + if (reg07 == 0) /* Bit Rate Control not enabled? */ + reg07 = 0x32; /* Allow 98 bytes / unit */ + else if (reg07 > 2) + reg07 -= 2; /* Decrease allowed bytes / unit */ + else + change = 0; } else { /* no overflow */ - if (reg07 != 0) { /* if not max */ - good++; - if (good >= 10) { - good = 0; + good++; + if (good >= 10) { + good = 0; + if (reg07) { /* BRC enabled? */ change = 1; - reg07 += 2; - switch (reg07) { - case 0x30: - if (sd->sensor == SENSOR_PAS202B) - reg07 += 2; - break; - case 0x32: - case 0x34: + if (reg07 < 0x32) + reg07 += 2; + else reg07 = 0; - break; - } - } - } else { /* reg07 max */ - if (sd->reg08 < sizeof jpeg_qual - 1) { - good++; - if (good > 10) { - sd->reg08++; - change = 2; - } } } } if (change) { - if (change & 1) { - reg_w(gspca_dev, reg07, 0x0007); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present - || !gspca_dev->streaming) - goto err; - } - if (change & 2) { - reg_w(gspca_dev, sd->reg08, - ZC3XX_R008_CLOCKSETTING); - if (gspca_dev->usb_err < 0 - || !gspca_dev->present - || !gspca_dev->streaming) - goto err; - sd->ctrls[QUALITY].val = jpeg_qual[sd->reg08]; - jpeg_set_qual(sd->jpeg_hdr, - jpeg_qual[sd->reg08]); - } + gspca_dev->usb_err = 0; + reg_w(gspca_dev, reg07, 0x0007); + if (gspca_dev->usb_err) + goto err; } mutex_unlock(&gspca_dev->usb_lock); } @@ -6503,7 +6321,6 @@ static int sd_config(struct gspca_dev *gspca_dev, /* define some sensors from the vendor/product */ sd->sensor = id->driver_info; - gspca_dev->cam.ctrls = sd->ctrls; sd->reg08 = REG08_DEF; INIT_WORK(&sd->work, transfer_update); @@ -6511,12 +6328,87 @@ static int sd_config(struct gspca_dev *gspca_dev, return 0; } -/* this function is called at probe and resume time */ -static int sd_init(struct gspca_dev *gspca_dev) +static int zcxx_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { - struct sd *sd = (struct sd *) gspca_dev; - struct cam *cam; - int sensor; + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + + switch (ctrl->id) { + case V4L2_CID_AUTOGAIN: + gspca_dev->usb_err = 0; + if (ctrl->val && sd->exposure && gspca_dev->streaming) + sd->exposure->val = getexposure(gspca_dev); + return gspca_dev->usb_err; + } + return -EINVAL; +} + +static int zcxx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct gspca_dev *gspca_dev = + container_of(ctrl->handler, struct gspca_dev, ctrl_handler); + struct sd *sd = (struct sd *)gspca_dev; + int i, qual; + + gspca_dev->usb_err = 0; + + if (ctrl->id == V4L2_CID_JPEG_COMPRESSION_QUALITY) { + qual = sd->reg08 >> 1; + + for (i = 0; i < ARRAY_SIZE(jpeg_qual); i++) { + if (ctrl->val <= jpeg_qual[i]) + break; + } + if (i > 0 && i == qual && ctrl->val < jpeg_qual[i]) + i--; + + /* With high quality settings we need max bandwidth */ + if (i >= 2 && gspca_dev->streaming && + !gspca_dev->cam.needs_full_bandwidth) + return -EBUSY; + + sd->reg08 = (i << 1) | 1; + ctrl->val = jpeg_qual[i]; + } + + if (!gspca_dev->streaming) + return 0; + + switch (ctrl->id) { + /* gamma/brightness/contrast cluster */ + case V4L2_CID_GAMMA: + setcontrast(gspca_dev, sd->gamma->val, + sd->brightness->val, sd->contrast->val); + break; + /* autogain/exposure cluster */ + case V4L2_CID_AUTOGAIN: + setautogain(gspca_dev, ctrl->val); + if (!gspca_dev->usb_err && !ctrl->val && sd->exposure) + setexposure(gspca_dev, sd->exposure->val); + break; + case V4L2_CID_POWER_LINE_FREQUENCY: + setlightfreq(gspca_dev, ctrl->val); + break; + case V4L2_CID_SHARPNESS: + setsharpness(gspca_dev, ctrl->val); + break; + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + setquality(gspca_dev); + break; + } + return gspca_dev->usb_err; +} + +static const struct v4l2_ctrl_ops zcxx_ctrl_ops = { + .g_volatile_ctrl = zcxx_g_volatile_ctrl, + .s_ctrl = zcxx_s_ctrl, +}; + +static int sd_init_controls(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *)gspca_dev; + struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; static const u8 gamma[SENSOR_MAX] = { [SENSOR_ADCM2700] = 4, [SENSOR_CS2102] = 4, @@ -6538,6 +6430,48 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_PO2030] = 4, [SENSOR_TAS5130C] = 3, }; + + gspca_dev->vdev.ctrl_handler = hdl; + v4l2_ctrl_handler_init(hdl, 8); + sd->brightness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + sd->contrast = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 128); + sd->gamma = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_GAMMA, 1, 6, 1, gamma[sd->sensor]); + if (sd->sensor == SENSOR_HV7131R) + sd->exposure = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_EXPOSURE, 0x30d, 0x493e, 1, 0x927); + sd->autogain = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_AUTOGAIN, 0, 1, 1, 1); + if (sd->sensor != SENSOR_OV7630C) + sd->plfreq = v4l2_ctrl_new_std_menu(hdl, &zcxx_ctrl_ops, + V4L2_CID_POWER_LINE_FREQUENCY, + V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 0, + V4L2_CID_POWER_LINE_FREQUENCY_DISABLED); + sd->sharpness = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_SHARPNESS, 0, 3, 1, + sd->sensor == SENSOR_PO2030 ? 0 : 2); + sd->jpegqual = v4l2_ctrl_new_std(hdl, &zcxx_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, + jpeg_qual[0], jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1], 1, + jpeg_qual[REG08_DEF >> 1]); + if (hdl->error) { + pr_err("Could not initialize controls\n"); + return hdl->error; + } + v4l2_ctrl_cluster(3, &sd->gamma); + if (sd->sensor == SENSOR_HV7131R) + v4l2_ctrl_auto_cluster(2, &sd->autogain, 0, true); + 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; + struct cam *cam; + int sensor; static const u8 mode_tb[SENSOR_MAX] = { [SENSOR_ADCM2700] = 2, [SENSOR_CS2102] = 1, @@ -6559,27 +6493,6 @@ static int sd_init(struct gspca_dev *gspca_dev) [SENSOR_PO2030] = 1, [SENSOR_TAS5130C] = 1, }; - static const u8 reg08_tb[SENSOR_MAX] = { - [SENSOR_ADCM2700] = 1, - [SENSOR_CS2102] = 3, - [SENSOR_CS2102K] = 3, - [SENSOR_GC0303] = 2, - [SENSOR_GC0305] = 3, - [SENSOR_HDCS2020] = 1, - [SENSOR_HV7131B] = 3, - [SENSOR_HV7131R] = 3, - [SENSOR_ICM105A] = 3, - [SENSOR_MC501CB] = 3, - [SENSOR_MT9V111_1] = 3, - [SENSOR_MT9V111_3] = 3, - [SENSOR_OV7620] = 1, - [SENSOR_OV7630C] = 3, - [SENSOR_PAS106] = 3, - [SENSOR_PAS202B] = 3, - [SENSOR_PB0330] = 3, - [SENSOR_PO2030] = 2, - [SENSOR_TAS5130C] = 3, - }; sensor = zcxx_probeSensor(gspca_dev); if (sensor >= 0) @@ -6688,7 +6601,6 @@ static int sd_init(struct gspca_dev *gspca_dev) case 0x2030: PDEBUG(D_PROBE, "Find Sensor PO2030"); sd->sensor = SENSOR_PO2030; - sd->ctrls[SHARPNESS].def = 0; /* from win traces */ break; case 0x7620: PDEBUG(D_PROBE, "Find Sensor OV7620"); @@ -6730,36 +6642,18 @@ static int sd_init(struct gspca_dev *gspca_dev) break; } - sd->ctrls[GAMMA].def = gamma[sd->sensor]; - sd->reg08 = reg08_tb[sd->sensor]; - sd->ctrls[QUALITY].def = jpeg_qual[sd->reg08]; - sd->ctrls[QUALITY].min = jpeg_qual[0]; - sd->ctrls[QUALITY].max = jpeg_qual[ARRAY_SIZE(jpeg_qual) - 1]; - - switch (sd->sensor) { - case SENSOR_HV7131R: - gspca_dev->ctrl_dis = (1 << QUALITY); - break; - case SENSOR_OV7630C: - gspca_dev->ctrl_dis = (1 << LIGHTFREQ) | (1 << EXPOSURE); - break; - case SENSOR_PAS202B: - gspca_dev->ctrl_dis = (1 << QUALITY) | (1 << EXPOSURE); - break; - default: - gspca_dev->ctrl_dis = (1 << EXPOSURE); - break; - } -#if AUTOGAIN_DEF - if (sd->ctrls[AUTOGAIN].val) - gspca_dev->ctrl_inac = (1 << EXPOSURE); -#endif - /* switch off the led */ reg_w(gspca_dev, 0x01, 0x0000); return gspca_dev->usb_err; } +static int sd_pre_start(struct gspca_dev *gspca_dev) +{ + struct sd *sd = (struct sd *) gspca_dev; + gspca_dev->cam.needs_full_bandwidth = (sd->reg08 >= 4) ? 1 : 0; + return 0; +} + static int sd_start(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *) gspca_dev; @@ -6864,7 +6758,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x03, 0x0008); break; } - setsharpness(gspca_dev); + setsharpness(gspca_dev, v4l2_ctrl_g_ctrl(sd->sharpness)); /* set the gamma tables when not set */ switch (sd->sensor) { @@ -6873,7 +6767,9 @@ static int sd_start(struct gspca_dev *gspca_dev) case SENSOR_OV7630C: break; default: - setcontrast(gspca_dev); + setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->gamma), + v4l2_ctrl_g_ctrl(sd->brightness), + v4l2_ctrl_g_ctrl(sd->contrast)); break; } setmatrix(gspca_dev); /* one more time? */ @@ -6885,8 +6781,10 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } setquality(gspca_dev); - jpeg_set_qual(sd->jpeg_hdr, jpeg_qual[sd->reg08]); - setlightfreq(gspca_dev); + /* Start with BRC disabled, transfer_update will enable it if needed */ + reg_w(gspca_dev, 0x00, 0x0007); + if (sd->plfreq) + setlightfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->plfreq)); switch (sd->sensor) { case SENSOR_ADCM2700: @@ -6897,7 +6795,7 @@ static int sd_start(struct gspca_dev *gspca_dev) reg_w(gspca_dev, 0x40, 0x0117); break; case SENSOR_HV7131R: - setexposure(gspca_dev); + setexposure(gspca_dev, v4l2_ctrl_g_ctrl(sd->exposure)); reg_w(gspca_dev, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN); break; case SENSOR_GC0305: @@ -6921,21 +6819,16 @@ static int sd_start(struct gspca_dev *gspca_dev) break; } - setautogain(gspca_dev); + setautogain(gspca_dev, v4l2_ctrl_g_ctrl(sd->autogain)); - /* start the transfer update thread if needed */ - if (gspca_dev->usb_err >= 0) { - switch (sd->sensor) { - case SENSOR_HV7131R: - case SENSOR_PAS202B: - sd->work_thread = - create_singlethread_workqueue(KBUILD_MODNAME); - queue_work(sd->work_thread, &sd->work); - break; - } - } + if (gspca_dev->usb_err < 0) + return gspca_dev->usb_err; - return gspca_dev->usb_err; + /* Start the transfer parameters update thread */ + sd->work_thread = create_singlethread_workqueue(KBUILD_MODNAME); + queue_work(sd->work_thread, &sd->work); + + return 0; } /* called on streamoff with alt 0 and on disconnect */ @@ -6949,7 +6842,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev) mutex_lock(&gspca_dev->usb_lock); sd->work_thread = NULL; } - if (!gspca_dev->present) + if (!gspca_dev->dev) return; send_unknown(gspca_dev, sd->sensor); } @@ -6987,72 +6880,17 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, gspca_frame_add(gspca_dev, INTER_PACKET, data, len); } -static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - - sd->ctrls[AUTOGAIN].val = val; - if (val) { - gspca_dev->ctrl_inac |= (1 << EXPOSURE); - } else { - gspca_dev->ctrl_inac &= ~(1 << EXPOSURE); - if (gspca_dev->streaming) - getexposure(gspca_dev); - } - if (gspca_dev->streaming) - setautogain(gspca_dev); - return gspca_dev->usb_err; -} - -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; -} - -static int sd_setquality(struct gspca_dev *gspca_dev, __s32 val) -{ - struct sd *sd = (struct sd *) gspca_dev; - int i; - - for (i = 0; i < ARRAY_SIZE(jpeg_qual) - 1; i++) { - if (val <= jpeg_qual[i]) - break; - } - if (i > 0 - && i == sd->reg08 - && val < jpeg_qual[sd->reg08]) - i--; - sd->reg08 = i; - sd->ctrls[QUALITY].val = jpeg_qual[i]; - if (gspca_dev->streaming) - jpeg_set_qual(sd->jpeg_hdr, sd->ctrls[QUALITY].val); - return gspca_dev->usb_err; -} - static int sd_set_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; + int ret; - sd_setquality(gspca_dev, jcomp->quality); - jcomp->quality = sd->ctrls[QUALITY].val; - return gspca_dev->usb_err; + ret = v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); + if (ret) + return ret; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); + return 0; } static int sd_get_jcomp(struct gspca_dev *gspca_dev, @@ -7061,7 +6899,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); - jcomp->quality = sd->ctrls[QUALITY].val; + jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; @@ -7085,14 +6923,13 @@ static int sd_int_pkt_scan(struct gspca_dev *gspca_dev, static const struct sd_desc sd_desc = { .name = KBUILD_MODNAME, - .ctrls = sd_ctrls, - .nctrls = ARRAY_SIZE(sd_ctrls), .config = sd_config, .init = sd_init, + .init_controls = sd_init_controls, + .isoc_init = sd_pre_start, .start = sd_start, .stop0 = sd_stop0, .pkt_scan = sd_pkt_scan, - .querymenu = sd_querymenu, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, #if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE) @@ -7176,6 +7013,7 @@ static struct usb_driver sd_driver = { #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, + .reset_resume = gspca_resume, #endif }; diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c index 068df4ba3f51..ae8f229d1141 100644 --- a/drivers/media/video/hdpvr/hdpvr-control.c +++ b/drivers/media/video/hdpvr/hdpvr-control.c @@ -113,6 +113,8 @@ int get_input_lines_info(struct hdpvr_device *dev) "get input lines info returned: %d, %s\n", ret, print_buf); } +#else + (void)ret; /* suppress compiler warning */ #endif lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0]; mutex_unlock(&dev->usbc_mutex); diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c index 11ffe9cc1780..0e9e156bb2aa 100644 --- a/drivers/media/video/hdpvr/hdpvr-video.c +++ b/drivers/media/video/hdpvr/hdpvr-video.c @@ -994,7 +994,7 @@ static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3) default: return -EINVAL; } - return 0; + return ret; } static int vidioc_try_ext_ctrls(struct file *file, void *priv, diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c index a62322d5c0d8..366434f5647e 100644 --- a/drivers/media/video/hexium_gemini.c +++ b/drivers/media/video/hexium_gemini.c @@ -40,15 +40,15 @@ static int hexium_num; #define HEXIUM_INPUTS 9 static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { - { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; #define HEXIUM_AUDIOS 0 @@ -59,11 +59,6 @@ struct hexium_data u8 byte; }; -#define HEXIUM_CONTROLS 1 -static struct v4l2_queryctrl hexium_controls[] = { - { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 }, -}; - #define HEXIUM_GEMINI_V_1_0 1 #define HEXIUM_GEMINI_DUAL_V_1_0 2 @@ -76,7 +71,6 @@ struct hexium int cur_input; /* current input */ v4l2_std_id cur_std; /* current standard */ - int cur_bw; /* current black/white status */ }; /* Samsung KS0127B decoder default registers */ @@ -119,18 +113,10 @@ static struct hexium_data hexium_pal[] = { { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } }; -static struct hexium_data hexium_pal_bw[] = { - { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } -}; - static struct hexium_data hexium_ntsc[] = { { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } }; -static struct hexium_data hexium_ntsc_bw[] = { - { 0x01, 0x53 }, { 0x12, 0x04 }, { 0x2D, 0x23 }, { 0x2E, 0x81 }, { -1 , 0xFF } -}; - static struct hexium_data hexium_secam[] = { { 0x01, 0x52 }, { 0x12, 0x64 }, { 0x2D, 0x2C }, { 0x2E, 0x9B }, { -1 , 0xFF } }; @@ -264,93 +250,6 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) return 0; } -/* the saa7146 provides some controls (brightness, contrast, saturation) - which gets registered *after* this function. because of this we have - to return with a value != 0 even if the function succeeded.. */ -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - int i; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == qc->id) { - *qc = hexium_controls[i]; - DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id); - return 0; - } - } - return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc); -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - int i; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_PRIVATE_BASE) { - vc->value = hexium->cur_bw; - DEB_D("VIDIOC_G_CTRL BW:%d\n", vc->value); - return 0; - } - return -EINVAL; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct hexium *hexium = (struct hexium *) dev->ext_priv; - int i = 0; - - for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) { - if (hexium_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_PRIVATE_BASE) - hexium->cur_bw = vc->value; - - DEB_D("VIDIOC_S_CTRL BW:%d\n", hexium->cur_bw); - - if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { - hexium_set_standard(hexium, hexium_pal); - return 0; - } - if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { - hexium_set_standard(hexium, hexium_ntsc); - return 0; - } - if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) { - hexium_set_standard(hexium, hexium_secam); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) { - hexium_set_standard(hexium, hexium_pal_bw); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) { - hexium_set_standard(hexium, hexium_ntsc_bw); - return 0; - } - if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) - /* fixme: is there no bw secam mode? */ - return -EINVAL; - - return -EINVAL; -} - - static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ @@ -399,12 +298,10 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d hexium->cur_input = 0; saa7146_vv_init(dev, &vv_data); - vv_data.ops.vidioc_queryctrl = vidioc_queryctrl; - vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl; - vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl; - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; + + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; ret = saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER); if (ret < 0) { pr_err("cannot register capture v4l2 device. skipping.\n"); diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c index 23debc967d94..a1eb26d11070 100644 --- a/drivers/media/video/hexium_orion.c +++ b/drivers/media/video/hexium_orion.c @@ -41,15 +41,15 @@ static int hexium_num; #define HEXIUM_INPUTS 9 static struct v4l2_input hexium_inputs[HEXIUM_INPUTS] = { - { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { 0, "CVBS 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 1, "CVBS 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 2, "CVBS 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 3, "CVBS 4", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 4, "CVBS 5", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 5, "CVBS 6", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 6, "Y/C 1", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 7, "Y/C 2", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { 8, "Y/C 3", V4L2_INPUT_TYPE_CAMERA, 0, 0, V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; #define HEXIUM_AUDIOS 0 @@ -371,9 +371,9 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d DEB_EE("\n"); saa7146_vv_init(dev, &vv_data); - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) { pr_err("cannot register capture v4l2 device. skipping.\n"); return -1; diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c index 679262ed13bc..057929e165ab 100644 --- a/drivers/media/video/ivtv/ivtv-driver.c +++ b/drivers/media/video/ivtv/ivtv-driver.c @@ -1201,9 +1201,9 @@ static int __devinit ivtv_probe(struct pci_dev *pdev, struct v4l2_ctrl_handler *hdl = itv->v4l2_dev.ctrl_handler; itv->ctrl_pts = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, - V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 1, 0); + V4L2_CID_MPEG_VIDEO_DEC_PTS, 0, 0, 0, 0); itv->ctrl_frame = v4l2_ctrl_new_std(hdl, &ivtv_hdl_out_ops, - V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0x7fffffff, 1, 0); + V4L2_CID_MPEG_VIDEO_DEC_FRAME, 0, 0, 0, 0); /* Note: V4L2_MPEG_AUDIO_DEC_PLAYBACK_AUTO is not supported, mask that menu item. */ itv->ctrl_audio_playback = diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c index c9663e885b9f..9ff69b5a87e2 100644 --- a/drivers/media/video/ivtv/ivtv-fileops.c +++ b/drivers/media/video/ivtv/ivtv-fileops.c @@ -746,8 +746,9 @@ unsigned int ivtv_v4l2_dec_poll(struct file *filp, poll_table *wait) return res; } -unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) +unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct ivtv_open_id *id = fh2id(filp->private_data); struct ivtv *itv = id->itv; struct ivtv_stream *s = &itv->streams[id->type]; @@ -755,7 +756,8 @@ unsigned int ivtv_v4l2_enc_poll(struct file *filp, poll_table * wait) unsigned res = 0; /* Start a capture if there is none */ - if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags)) { + if (!eof && !test_bit(IVTV_F_S_STREAMING, &s->s_flags) && + (req_events & (POLLIN | POLLRDNORM))) { int rc; rc = ivtv_start_capture(id); diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index 989e556913ed..f7d57b3f2842 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c @@ -135,7 +135,6 @@ void ivtv_set_osd_alpha(struct ivtv *itv) int ivtv_set_speed(struct ivtv *itv, int speed) { u32 data[CX2341X_MBOX_MAX_DATA]; - struct ivtv_stream *s; int single_step = (speed == 1 || speed == -1); DEFINE_WAIT(wait); @@ -145,8 +144,6 @@ int ivtv_set_speed(struct ivtv *itv, int speed) if (speed == itv->speed && !single_step) return 0; - s = &itv->streams[IVTV_DEC_STREAM_TYPE_MPG]; - if (single_step && (speed < 0) == (itv->speed < 0)) { /* Single step video and no need to change direction */ ivtv_vapi(itv, CX2341X_DEC_STEP_VIDEO, 1, 0); @@ -1468,8 +1465,9 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti switch (sub->type) { case V4L2_EVENT_VSYNC: case V4L2_EVENT_EOS: + return v4l2_event_subscribe(fh, sub, 0, NULL); case V4L2_EVENT_CTRL: - return v4l2_event_subscribe(fh, sub, 0); + return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops); default: return -EINVAL; } @@ -1827,7 +1825,7 @@ static long ivtv_default(struct file *file, void *fh, bool valid_prio, return ivtv_decoder_ioctls(file, cmd, (void *)arg); default: - return -EINVAL; + return -ENOTTY; } return 0; } diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c index 7ea5ca7f012b..6738592aa35d 100644 --- a/drivers/media/video/ivtv/ivtv-streams.c +++ b/drivers/media/video/ivtv/ivtv-streams.c @@ -228,6 +228,10 @@ static int ivtv_prep_dev(struct ivtv *itv, int type) s->vdev->release = video_device_release; s->vdev->tvnorms = V4L2_STD_ALL; s->vdev->lock = &itv->serialize_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &s->vdev->flags); set_bit(V4L2_FL_USE_FH_PRIO, &s->vdev->flags); ivtv_set_funcs(s->vdev); return 0; diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c index e5e7fa9e737b..05b94aa8ba32 100644 --- a/drivers/media/video/ivtv/ivtvfb.c +++ b/drivers/media/video/ivtv/ivtvfb.c @@ -1293,6 +1293,7 @@ static int __init ivtvfb_init(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, ®istered, ivtvfb_callback_init); + (void)err; /* suppress compiler warning */ if (!registered) { printk(KERN_ERR "ivtvfb: no cards found\n"); return -ENODEV; @@ -1309,6 +1310,7 @@ static void ivtvfb_cleanup(void) drv = driver_find("ivtv", &pci_bus_type); err = driver_for_each_device(drv, NULL, NULL, ivtvfb_callback_cleanup); + (void)err; /* suppress compiler warning */ } module_init(ivtvfb_init); diff --git a/drivers/media/video/m5mols/m5mols.h b/drivers/media/video/m5mols/m5mols.h index 4b021e1ee5f2..bb589917b65b 100644 --- a/drivers/media/video/m5mols/m5mols.h +++ b/drivers/media/video/m5mols/m5mols.h @@ -21,11 +21,6 @@ extern int m5mols_debug; -#define to_m5mols(__sd) container_of(__sd, struct m5mols_info, sd) - -#define to_sd(__ctrl) \ - (&container_of(__ctrl->handler, struct m5mols_info, handle)->sd) - enum m5mols_restype { M5MOLS_RESTYPE_MONITOR, M5MOLS_RESTYPE_CAPTURE, @@ -163,21 +158,27 @@ struct m5mols_version { * @ffmt: current fmt according to resolution type * @res_type: current resolution type * @irq_waitq: waitqueue for the capture - * @flags: state variable for the interrupt handler + * @irq_done: set to 1 in the interrupt handler * @handle: control handler - * @autoexposure: Auto Exposure control - * @exposure: Exposure control - * @autowb: Auto White Balance control - * @colorfx: Color effect control - * @saturation: Saturation control - * @zoom: Zoom control + * @auto_exposure: auto/manual exposure control + * @exposure_bias: exposure compensation control + * @exposure: manual exposure control + * @metering: exposure metering control + * @auto_iso: auto/manual ISO sensitivity control + * @iso: manual ISO sensitivity control + * @auto_wb: auto white balance control + * @lock_3a: 3A lock control + * @colorfx: color effect control + * @saturation: saturation control + * @zoom: zoom control + * @wdr: wide dynamic range control + * @stabilization: image stabilization control + * @jpeg_quality: JPEG compression quality control * @ver: information of the version * @cap: the capture mode attributes - * @power: current sensor's power status * @isp_ready: 1 when the ISP controller has completed booting + * @power: current sensor's power status * @ctrl_sync: 1 when the control handler state is restored in H/W - * @lock_ae: true means the Auto Exposure is locked - * @lock_awb: true means the Aut WhiteBalance is locked * @resolution: register value for current resolution * @mode: register value for current operation mode * @set_power: optional power callback to the board code @@ -193,15 +194,27 @@ struct m5mols_info { atomic_t irq_done; struct v4l2_ctrl_handler handle; + struct { + /* exposure/exposure bias/auto exposure cluster */ + struct v4l2_ctrl *auto_exposure; + struct v4l2_ctrl *exposure_bias; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *metering; + }; + struct { + /* iso/auto iso cluster */ + struct v4l2_ctrl *auto_iso; + struct v4l2_ctrl *iso; + }; + struct v4l2_ctrl *auto_wb; - /* Autoexposure/exposure control cluster */ - struct v4l2_ctrl *autoexposure; - struct v4l2_ctrl *exposure; - - struct v4l2_ctrl *autowb; + struct v4l2_ctrl *lock_3a; struct v4l2_ctrl *colorfx; struct v4l2_ctrl *saturation; struct v4l2_ctrl *zoom; + struct v4l2_ctrl *wdr; + struct v4l2_ctrl *stabilization; + struct v4l2_ctrl *jpeg_quality; struct m5mols_version ver; struct m5mols_capture cap; @@ -210,8 +223,6 @@ struct m5mols_info { unsigned int power:1; unsigned int ctrl_sync:1; - bool lock_ae; - bool lock_awb; u8 resolution; u8 mode; @@ -282,7 +293,7 @@ int m5mols_busy_wait(struct v4l2_subdev *sd, u32 reg, u32 value, u32 mask, * The available executing order between each modes are as follows: * PARAMETER <---> MONITOR <---> CAPTURE */ -int m5mols_mode(struct m5mols_info *info, u8 mode); +int m5mols_set_mode(struct m5mols_info *info, u8 mode); int m5mols_enable_interrupt(struct v4l2_subdev *sd, u8 reg); int m5mols_wait_interrupt(struct v4l2_subdev *sd, u8 condition, u32 timeout); @@ -291,9 +302,33 @@ int m5mols_start_capture(struct m5mols_info *info); int m5mols_do_scenemode(struct m5mols_info *info, u8 mode); int m5mols_lock_3a(struct m5mols_info *info, bool lock); int m5mols_set_ctrl(struct v4l2_ctrl *ctrl); +int m5mols_init_controls(struct v4l2_subdev *sd); /* The firmware function */ int m5mols_update_fw(struct v4l2_subdev *sd, int (*set_power)(struct m5mols_info *, bool)); +static inline struct m5mols_info *to_m5mols(struct v4l2_subdev *subdev) +{ + return container_of(subdev, struct m5mols_info, sd); +} + +static inline struct v4l2_subdev *to_sd(struct v4l2_ctrl *ctrl) +{ + struct m5mols_info *info = container_of(ctrl->handler, + struct m5mols_info, handle); + return &info->sd; +} + +static inline void m5mols_set_ctrl_mode(struct v4l2_ctrl *ctrl, + unsigned int mode) +{ + ctrl->priv = (void *)mode; +} + +static inline unsigned int m5mols_get_ctrl_mode(struct v4l2_ctrl *ctrl) +{ + return (unsigned int)ctrl->priv; +} + #endif /* M5MOLS_H */ diff --git a/drivers/media/video/m5mols/m5mols_capture.c b/drivers/media/video/m5mols/m5mols_capture.c index ba25e8e2ba4c..cb243bd278ce 100644 --- a/drivers/media/video/m5mols/m5mols_capture.c +++ b/drivers/media/video/m5mols/m5mols_capture.c @@ -106,7 +106,6 @@ static int m5mols_capture_info(struct m5mols_info *info) int m5mols_start_capture(struct m5mols_info *info) { struct v4l2_subdev *sd = &info->sd; - u8 resolution = info->resolution; int ret; /* @@ -114,22 +113,18 @@ int m5mols_start_capture(struct m5mols_info *info) * format. The frame capture is initiated during switching from Monitor * to Capture mode. */ - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_restore_controls(info); if (!ret) ret = m5mols_write(sd, CAPP_YUVOUT_MAIN, REG_JPEG); if (!ret) - ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, resolution); + ret = m5mols_write(sd, CAPP_MAIN_IMAGE_SIZE, info->resolution); if (!ret) - ret = m5mols_lock_3a(info, true); - if (!ret) - ret = m5mols_mode(info, REG_CAPTURE); + ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) /* Wait until a frame is captured to ISP internal memory */ ret = m5mols_wait_interrupt(sd, REG_INT_CAPTURE, 2000); - if (!ret) - ret = m5mols_lock_3a(info, false); if (ret) return ret; diff --git a/drivers/media/video/m5mols/m5mols_controls.c b/drivers/media/video/m5mols/m5mols_controls.c index d135d20d09cf..392a028730e2 100644 --- a/drivers/media/video/m5mols/m5mols_controls.c +++ b/drivers/media/video/m5mols/m5mols_controls.c @@ -139,7 +139,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (mode > REG_SCENE_CANDLE) return -EINVAL; - ret = m5mols_lock_3a(info, false); + ret = v4l2_ctrl_s_ctrl(info->lock_3a, 0); if (!ret) ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, mode); if (!ret) @@ -169,7 +169,7 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (!ret) ret = m5mols_write(sd, AE_ISO, scenemode.iso); if (!ret) - ret = m5mols_mode(info, REG_CAPTURE); + ret = m5mols_set_mode(info, REG_CAPTURE); if (!ret) ret = m5mols_write(sd, CAPP_WDR_EN, scenemode.wdr); if (!ret) @@ -181,119 +181,448 @@ int m5mols_do_scenemode(struct m5mols_info *info, u8 mode) if (!ret) ret = m5mols_write(sd, CAPC_MODE, scenemode.capt_mode); if (!ret) - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); return ret; } -static int m5mols_lock_ae(struct m5mols_info *info, bool lock) +static int m5mols_3a_lock(struct m5mols_info *info, struct v4l2_ctrl *ctrl) { + bool af_lock = ctrl->val & V4L2_LOCK_FOCUS; int ret = 0; - if (info->lock_ae != lock) - ret = m5mols_write(&info->sd, AE_LOCK, - lock ? REG_AE_LOCK : REG_AE_UNLOCK); - if (!ret) - info->lock_ae = lock; + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_EXPOSURE) { + bool ae_lock = ctrl->val & V4L2_LOCK_EXPOSURE; - return ret; -} + ret = m5mols_write(&info->sd, AE_LOCK, ae_lock ? + REG_AE_LOCK : REG_AE_UNLOCK); + if (ret) + return ret; + } -static int m5mols_lock_awb(struct m5mols_info *info, bool lock) -{ - int ret = 0; + if (((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_WHITE_BALANCE) + && info->auto_wb->val) { + bool awb_lock = ctrl->val & V4L2_LOCK_WHITE_BALANCE; - if (info->lock_awb != lock) - ret = m5mols_write(&info->sd, AWB_LOCK, - lock ? REG_AWB_LOCK : REG_AWB_UNLOCK); - if (!ret) - info->lock_awb = lock; + ret = m5mols_write(&info->sd, AWB_LOCK, awb_lock ? + REG_AWB_LOCK : REG_AWB_UNLOCK); + if (ret) + return ret; + } - return ret; -} + if (!info->ver.af || !af_lock) + return ret; -/* m5mols_lock_3a() - Lock 3A(Auto Exposure, Auto Whitebalance, Auto Focus) */ -int m5mols_lock_3a(struct m5mols_info *info, bool lock) -{ - int ret; - - ret = m5mols_lock_ae(info, lock); - if (!ret) - ret = m5mols_lock_awb(info, lock); - /* Don't need to handle unlocking AF */ - if (!ret && is_available_af(info) && lock) + if ((ctrl->val ^ ctrl->cur.val) & V4L2_LOCK_FOCUS) ret = m5mols_write(&info->sd, AF_EXECUTE, REG_AF_STOP); return ret; } -/* m5mols_set_ctrl() - The main s_ctrl function called by m5mols_set_ctrl() */ -int m5mols_set_ctrl(struct v4l2_ctrl *ctrl) +static int m5mols_set_metering_mode(struct m5mols_info *info, int mode) +{ + unsigned int metering; + + switch (mode) { + case V4L2_EXPOSURE_METERING_CENTER_WEIGHTED: + metering = REG_AE_CENTER; + break; + case V4L2_EXPOSURE_METERING_SPOT: + metering = REG_AE_SPOT; + break; + default: + metering = REG_AE_ALL; + break; + } + + return m5mols_write(&info->sd, AE_MODE, metering); +} + +static int m5mols_set_exposure(struct m5mols_info *info, int exposure) +{ + struct v4l2_subdev *sd = &info->sd; + int ret = 0; + + if (exposure == V4L2_EXPOSURE_AUTO) { + /* Unlock auto exposure */ + info->lock_3a->val &= ~V4L2_LOCK_EXPOSURE; + m5mols_3a_lock(info, info->lock_3a); + + ret = m5mols_set_metering_mode(info, info->metering->val); + if (ret < 0) + return ret; + + v4l2_dbg(1, m5mols_debug, sd, + "%s: exposure bias: %#x, metering: %#x\n", + __func__, info->exposure_bias->val, + info->metering->val); + + return m5mols_write(sd, AE_INDEX, info->exposure_bias->val); + } + + if (exposure == V4L2_EXPOSURE_MANUAL) { + ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_MON, + info->exposure->val); + if (ret == 0) + ret = m5mols_write(sd, AE_MAN_GAIN_CAP, + info->exposure->val); + + v4l2_dbg(1, m5mols_debug, sd, "%s: exposure: %#x\n", + __func__, info->exposure->val); + } + + return ret; +} + +static int m5mols_set_white_balance(struct m5mols_info *info, int val) +{ + static const unsigned short wb[][2] = { + { V4L2_WHITE_BALANCE_INCANDESCENT, REG_AWB_INCANDESCENT }, + { V4L2_WHITE_BALANCE_FLUORESCENT, REG_AWB_FLUORESCENT_1 }, + { V4L2_WHITE_BALANCE_FLUORESCENT_H, REG_AWB_FLUORESCENT_2 }, + { V4L2_WHITE_BALANCE_HORIZON, REG_AWB_HORIZON }, + { V4L2_WHITE_BALANCE_DAYLIGHT, REG_AWB_DAYLIGHT }, + { V4L2_WHITE_BALANCE_FLASH, REG_AWB_LEDLIGHT }, + { V4L2_WHITE_BALANCE_CLOUDY, REG_AWB_CLOUDY }, + { V4L2_WHITE_BALANCE_SHADE, REG_AWB_SHADE }, + { V4L2_WHITE_BALANCE_AUTO, REG_AWB_AUTO }, + }; + int i; + struct v4l2_subdev *sd = &info->sd; + int ret = -EINVAL; + + for (i = 0; i < ARRAY_SIZE(wb); i++) { + int awb; + if (wb[i][0] != val) + continue; + + v4l2_dbg(1, m5mols_debug, sd, + "Setting white balance to: %#x\n", wb[i][0]); + + awb = wb[i][0] == V4L2_WHITE_BALANCE_AUTO; + ret = m5mols_write(sd, AWB_MODE, awb ? REG_AWB_AUTO : + REG_AWB_PRESET); + if (ret < 0) + return ret; + + if (!awb) + ret = m5mols_write(sd, AWB_MANUAL, wb[i][1]); + } + + return ret; +} + +static int m5mols_set_saturation(struct m5mols_info *info, int val) +{ + int ret = m5mols_write(&info->sd, MON_CHROMA_LVL, val); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, MON_CHROMA_EN, REG_CHROMA_ON); +} + +static int m5mols_set_color_effect(struct m5mols_info *info, int val) +{ + unsigned int m_effect = REG_COLOR_EFFECT_OFF; + unsigned int p_effect = REG_EFFECT_OFF; + unsigned int cfix_r = 0, cfix_b = 0; + struct v4l2_subdev *sd = &info->sd; + int ret = 0; + + switch (val) { + case V4L2_COLORFX_BW: + m_effect = REG_COLOR_EFFECT_ON; + break; + case V4L2_COLORFX_NEGATIVE: + p_effect = REG_EFFECT_NEGA; + break; + case V4L2_COLORFX_EMBOSS: + p_effect = REG_EFFECT_EMBOSS; + break; + case V4L2_COLORFX_SEPIA: + m_effect = REG_COLOR_EFFECT_ON; + cfix_r = REG_CFIXR_SEPIA; + cfix_b = REG_CFIXB_SEPIA; + break; + } + + ret = m5mols_write(sd, PARM_EFFECT, p_effect); + if (!ret) + ret = m5mols_write(sd, MON_EFFECT, m_effect); + + if (ret == 0 && m_effect == REG_COLOR_EFFECT_ON) { + ret = m5mols_write(sd, MON_CFIXR, cfix_r); + if (!ret) + ret = m5mols_write(sd, MON_CFIXB, cfix_b); + } + + v4l2_dbg(1, m5mols_debug, sd, + "p_effect: %#x, m_effect: %#x, r: %#x, b: %#x (%d)\n", + p_effect, m_effect, cfix_r, cfix_b, ret); + + return ret; +} + +static int m5mols_set_iso(struct m5mols_info *info, int auto_iso) +{ + u32 iso = auto_iso ? 0 : info->iso->val + 1; + + return m5mols_write(&info->sd, AE_ISO, iso); +} + +static int m5mols_set_wdr(struct m5mols_info *info, int wdr) +{ + int ret; + + ret = m5mols_write(&info->sd, MON_TONE_CTL, wdr ? 9 : 5); + if (ret < 0) + return ret; + + ret = m5mols_set_mode(info, REG_CAPTURE); + if (ret < 0) + return ret; + + return m5mols_write(&info->sd, CAPP_WDR_EN, wdr); +} + +static int m5mols_set_stabilization(struct m5mols_info *info, int val) +{ + struct v4l2_subdev *sd = &info->sd; + unsigned int evp = val ? 0xe : 0x0; + int ret; + + ret = m5mols_write(sd, AE_EV_PRESET_MONITOR, evp); + if (ret < 0) + return ret; + + return m5mols_write(sd, AE_EV_PRESET_CAPTURE, evp); +} + +static int m5mols_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct v4l2_subdev *sd = to_sd(ctrl); struct m5mols_info *info = to_m5mols(sd); - int ret; + int ret = 0; + u8 status; + + v4l2_dbg(1, m5mols_debug, sd, "%s: ctrl: %s (%d)\n", + __func__, ctrl->name, info->isp_ready); + + if (!info->isp_ready) + return -EBUSY; switch (ctrl->id) { + case V4L2_CID_ISO_SENSITIVITY_AUTO: + ret = m5mols_read_u8(sd, AE_ISO, &status); + if (ret == 0) + ctrl->val = !status; + if (status != REG_ISO_AUTO) + info->iso->val = status - 1; + break; + + case V4L2_CID_3A_LOCK: + ctrl->val &= ~0x7; + + ret = m5mols_read_u8(sd, AE_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AWB_LOCK, &status); + if (ret) + return ret; + if (status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + + ret = m5mols_read_u8(sd, AF_EXECUTE, &status); + if (!status) + info->lock_3a->val |= V4L2_LOCK_EXPOSURE; + break; + } + + return ret; +} + +static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) +{ + unsigned int ctrl_mode = m5mols_get_ctrl_mode(ctrl); + struct v4l2_subdev *sd = to_sd(ctrl); + struct m5mols_info *info = to_m5mols(sd); + int last_mode = info->mode; + int ret = 0; + + /* + * If needed, defer restoring the controls until + * the device is fully initialized. + */ + if (!info->isp_ready) { + info->ctrl_sync = 0; + return 0; + } + + v4l2_dbg(1, m5mols_debug, sd, "%s: %s, val: %d, priv: %#x\n", + __func__, ctrl->name, ctrl->val, (int)ctrl->priv); + + if (ctrl_mode && ctrl_mode != info->mode) { + ret = m5mols_set_mode(info, ctrl_mode); + if (ret < 0) + return ret; + } + + switch (ctrl->id) { + case V4L2_CID_3A_LOCK: + ret = m5mols_3a_lock(info, ctrl); + break; + case V4L2_CID_ZOOM_ABSOLUTE: - return m5mols_write(sd, MON_ZOOM, ctrl->val); + ret = m5mols_write(sd, MON_ZOOM, ctrl->val); + break; case V4L2_CID_EXPOSURE_AUTO: - ret = m5mols_lock_ae(info, - ctrl->val == V4L2_EXPOSURE_AUTO ? false : true); - if (!ret && ctrl->val == V4L2_EXPOSURE_AUTO) - ret = m5mols_write(sd, AE_MODE, REG_AE_ALL); - if (!ret && ctrl->val == V4L2_EXPOSURE_MANUAL) { - int val = info->exposure->val; - ret = m5mols_write(sd, AE_MODE, REG_AE_OFF); - if (!ret) - ret = m5mols_write(sd, AE_MAN_GAIN_MON, val); - if (!ret) - ret = m5mols_write(sd, AE_MAN_GAIN_CAP, val); - } - return ret; + ret = m5mols_set_exposure(info, ctrl->val); + break; - case V4L2_CID_AUTO_WHITE_BALANCE: - ret = m5mols_lock_awb(info, ctrl->val ? false : true); - if (!ret) - ret = m5mols_write(sd, AWB_MODE, ctrl->val ? - REG_AWB_AUTO : REG_AWB_PRESET); - return ret; + case V4L2_CID_ISO_SENSITIVITY: + ret = m5mols_set_iso(info, ctrl->val); + break; + + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + ret = m5mols_set_white_balance(info, ctrl->val); + break; case V4L2_CID_SATURATION: - ret = m5mols_write(sd, MON_CHROMA_LVL, ctrl->val); - if (!ret) - ret = m5mols_write(sd, MON_CHROMA_EN, REG_CHROMA_ON); - return ret; + ret = m5mols_set_saturation(info, ctrl->val); + break; case V4L2_CID_COLORFX: - /* - * This control uses two kinds of registers: normal & color. - * The normal effect belongs to category 1, while the color - * one belongs to category 2. - * - * The normal effect uses one register: CAT1_EFFECT. - * The color effect uses three registers: - * CAT2_COLOR_EFFECT, CAT2_CFIXR, CAT2_CFIXB. - */ - ret = m5mols_write(sd, PARM_EFFECT, - ctrl->val == V4L2_COLORFX_NEGATIVE ? REG_EFFECT_NEGA : - ctrl->val == V4L2_COLORFX_EMBOSS ? REG_EFFECT_EMBOSS : - REG_EFFECT_OFF); - if (!ret) - ret = m5mols_write(sd, MON_EFFECT, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_COLOR_EFFECT_ON : REG_COLOR_EFFECT_OFF); - if (!ret) - ret = m5mols_write(sd, MON_CFIXR, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_CFIXR_SEPIA : 0); - if (!ret) - ret = m5mols_write(sd, MON_CFIXB, - ctrl->val == V4L2_COLORFX_SEPIA ? - REG_CFIXB_SEPIA : 0); + ret = m5mols_set_color_effect(info, ctrl->val); + break; + + case V4L2_CID_WIDE_DYNAMIC_RANGE: + ret = m5mols_set_wdr(info, ctrl->val); + break; + + case V4L2_CID_IMAGE_STABILIZATION: + ret = m5mols_set_stabilization(info, ctrl->val); + break; + + case V4L2_CID_JPEG_COMPRESSION_QUALITY: + ret = m5mols_write(sd, CAPP_JPEG_RATIO, ctrl->val); + break; + } + + if (ret == 0 && info->mode != last_mode) + ret = m5mols_set_mode(info, last_mode); + + return ret; +} + +static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { + .g_volatile_ctrl = m5mols_g_volatile_ctrl, + .s_ctrl = m5mols_s_ctrl, +}; + +/* Supported manual ISO values */ +static const s64 iso_qmenu[] = { + /* AE_ISO: 0x01...0x07 */ + 50, 100, 200, 400, 800, 1600, 3200 +}; + +/* Supported Exposure Bias values, -2.0EV...+2.0EV */ +static const s64 ev_bias_qmenu[] = { + /* AE_INDEX: 0x00...0x08 */ + -2000, -1500, -1000, -500, 0, 500, 1000, 1500, 2000 +}; + +int m5mols_init_controls(struct v4l2_subdev *sd) +{ + struct m5mols_info *info = to_m5mols(sd); + u16 exposure_max; + u16 zoom_step; + int ret; + + /* Determine the firmware dependant control range and step values */ + ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &exposure_max); + if (ret < 0) + return ret; + + zoom_step = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; + v4l2_ctrl_handler_init(&info->handle, 20); + + info->auto_wb = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE, + 9, ~0x3fe, V4L2_WHITE_BALANCE_AUTO); + + /* Exposure control cluster */ + info->auto_exposure = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, + 1, ~0x03, V4L2_EXPOSURE_AUTO); + + info->exposure = v4l2_ctrl_new_std(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, + 0, exposure_max, 1, exposure_max / 2); + + info->exposure_bias = v4l2_ctrl_new_int_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_AUTO_EXPOSURE_BIAS, + ARRAY_SIZE(ev_bias_qmenu) - 1, + ARRAY_SIZE(ev_bias_qmenu)/2 - 1, + ev_bias_qmenu); + + info->metering = v4l2_ctrl_new_std_menu(&info->handle, + &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_METERING, + 2, ~0x7, V4L2_EXPOSURE_METERING_AVERAGE); + + /* ISO control cluster */ + info->auto_iso = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY_AUTO, 1, ~0x03, 1); + + info->iso = v4l2_ctrl_new_int_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ISO_SENSITIVITY, ARRAY_SIZE(iso_qmenu) - 1, + ARRAY_SIZE(iso_qmenu)/2 - 1, iso_qmenu); + + info->saturation = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_SATURATION, 1, 5, 1, 3); + + info->zoom = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_ZOOM_ABSOLUTE, 1, 70, zoom_step, 1); + + info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_COLORFX, 4, 0, V4L2_COLORFX_NONE); + + info->wdr = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_WIDE_DYNAMIC_RANGE, 0, 1, 1, 0); + + info->stabilization = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_IMAGE_STABILIZATION, 0, 1, 1, 0); + + info->jpeg_quality = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_JPEG_COMPRESSION_QUALITY, 1, 100, 1, 80); + + info->lock_3a = v4l2_ctrl_new_std(&info->handle, &m5mols_ctrl_ops, + V4L2_CID_3A_LOCK, 0, 0x7, 0, 0); + + if (info->handle.error) { + int ret = info->handle.error; + v4l2_err(sd, "Failed to initialize controls: %d\n", ret); + v4l2_ctrl_handler_free(&info->handle); return ret; } - return -EINVAL; + v4l2_ctrl_auto_cluster(4, &info->auto_exposure, 1, false); + info->auto_iso->flags |= V4L2_CTRL_FLAG_VOLATILE | + V4L2_CTRL_FLAG_UPDATE; + v4l2_ctrl_auto_cluster(2, &info->auto_iso, 0, false); + + info->lock_3a->flags |= V4L2_CTRL_FLAG_VOLATILE; + + m5mols_set_ctrl_mode(info->auto_exposure, REG_PARAMETER); + m5mols_set_ctrl_mode(info->auto_wb, REG_PARAMETER); + m5mols_set_ctrl_mode(info->colorfx, REG_MONITOR); + + sd->ctrl_handler = &info->handle; + + return 0; } diff --git a/drivers/media/video/m5mols/m5mols_core.c b/drivers/media/video/m5mols/m5mols_core.c index d718aee01c77..ac7d28b6ddf2 100644 --- a/drivers/media/video/m5mols/m5mols_core.c +++ b/drivers/media/video/m5mols/m5mols_core.c @@ -362,14 +362,14 @@ static int m5mols_reg_mode(struct v4l2_subdev *sd, u8 mode) } /** - * m5mols_mode - manage the M-5MOLS's mode + * m5mols_set_mode - set the M-5MOLS controller mode * @mode: the required operation mode * * The commands of M-5MOLS are grouped into specific modes. Each functionality - * can be guaranteed only when the sensor is operating in mode which which - * a command belongs to. + * can be guaranteed only when the sensor is operating in mode which a command + * belongs to. */ -int m5mols_mode(struct m5mols_info *info, u8 mode) +int m5mols_set_mode(struct m5mols_info *info, u8 mode) { struct v4l2_subdev *sd = &info->sd; int ret = -EINVAL; @@ -645,13 +645,13 @@ static int m5mols_start_monitor(struct m5mols_info *info) struct v4l2_subdev *sd = &info->sd; int ret; - ret = m5mols_mode(info, REG_PARAMETER); + ret = m5mols_set_mode(info, REG_PARAMETER); if (!ret) ret = m5mols_write(sd, PARM_MON_SIZE, info->resolution); if (!ret) ret = m5mols_write(sd, PARM_MON_FPS, REG_FPS_30); if (!ret) - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_restore_controls(info); @@ -674,42 +674,13 @@ static int m5mols_s_stream(struct v4l2_subdev *sd, int enable) return ret; } - return m5mols_mode(info, REG_PARAMETER); + return m5mols_set_mode(info, REG_PARAMETER); } static const struct v4l2_subdev_video_ops m5mols_video_ops = { .s_stream = m5mols_s_stream, }; -static int m5mols_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct v4l2_subdev *sd = to_sd(ctrl); - struct m5mols_info *info = to_m5mols(sd); - int ispstate = info->mode; - int ret; - - /* - * If needed, defer restoring the controls until - * the device is fully initialized. - */ - if (!info->isp_ready) { - info->ctrl_sync = 0; - return 0; - } - - ret = m5mols_mode(info, REG_PARAMETER); - if (ret < 0) - return ret; - ret = m5mols_set_ctrl(ctrl); - if (ret < 0) - return ret; - return m5mols_mode(info, ispstate); -} - -static const struct v4l2_ctrl_ops m5mols_ctrl_ops = { - .s_ctrl = m5mols_s_ctrl, -}; - static int m5mols_sensor_power(struct m5mols_info *info, bool enable) { struct v4l2_subdev *sd = &info->sd; @@ -802,52 +773,6 @@ static int m5mols_fw_start(struct v4l2_subdev *sd) return ret; } -static int m5mols_init_controls(struct m5mols_info *info) -{ - struct v4l2_subdev *sd = &info->sd; - u16 max_exposure; - u16 step_zoom; - int ret; - - /* Determine value's range & step of controls for various FW version */ - ret = m5mols_read_u16(sd, AE_MAX_GAIN_MON, &max_exposure); - if (!ret) - step_zoom = is_manufacturer(info, REG_SAMSUNG_OPTICS) ? 31 : 1; - if (ret) - return ret; - - v4l2_ctrl_handler_init(&info->handle, 6); - info->autowb = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_AUTO_WHITE_BALANCE, - 0, 1, 1, 0); - info->saturation = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_SATURATION, - 1, 5, 1, 3); - info->zoom = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_ZOOM_ABSOLUTE, - 1, 70, step_zoom, 1); - info->exposure = v4l2_ctrl_new_std(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE, - 0, max_exposure, 1, (int)max_exposure/2); - info->colorfx = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_COLORFX, - 4, (1 << V4L2_COLORFX_BW), V4L2_COLORFX_NONE); - info->autoexposure = v4l2_ctrl_new_std_menu(&info->handle, - &m5mols_ctrl_ops, V4L2_CID_EXPOSURE_AUTO, - 1, 0, V4L2_EXPOSURE_AUTO); - - sd->ctrl_handler = &info->handle; - if (info->handle.error) { - v4l2_err(sd, "Failed to initialize controls: %d\n", ret); - v4l2_ctrl_handler_free(&info->handle); - return info->handle.error; - } - - v4l2_ctrl_cluster(2, &info->autoexposure); - - return 0; -} - /** * m5mols_s_power - Main sensor power control function * @@ -868,7 +793,7 @@ static int m5mols_s_power(struct v4l2_subdev *sd, int on) } if (is_manufacturer(info, REG_SAMSUNG_TECHWIN)) { - ret = m5mols_mode(info, REG_MONITOR); + ret = m5mols_set_mode(info, REG_MONITOR); if (!ret) ret = m5mols_write(sd, AF_EXECUTE, REG_AF_STOP); if (!ret) @@ -1010,7 +935,7 @@ static int __devinit m5mols_probe(struct i2c_client *client, ret = m5mols_fw_start(sd); if (!ret) - ret = m5mols_init_controls(info); + ret = m5mols_init_controls(sd); m5mols_sensor_power(info, false); if (!ret) diff --git a/drivers/media/video/m5mols/m5mols_reg.h b/drivers/media/video/m5mols/m5mols_reg.h index ae4aced0f9b2..14d4be72aeff 100644 --- a/drivers/media/video/m5mols/m5mols_reg.h +++ b/drivers/media/video/m5mols/m5mols_reg.h @@ -310,6 +310,7 @@ #define REG_JPEG 0x10 #define CAPP_MAIN_IMAGE_SIZE I2C_REG(CAT_CAPT_PARM, 0x01, 1) +#define CAPP_JPEG_RATIO I2C_REG(CAT_CAPT_PARM, 0x17, 1) #define CAPP_MCC_MODE I2C_REG(CAT_CAPT_PARM, 0x1d, 1) #define REG_MCC_OFF 0x00 diff --git a/drivers/media/video/marvell-ccic/mcam-core.c b/drivers/media/video/marvell-ccic/mcam-core.c index 996ac34d9a89..ce2b7b4788d6 100644 --- a/drivers/media/video/marvell-ccic/mcam-core.c +++ b/drivers/media/video/marvell-ccic/mcam-core.c @@ -1356,7 +1356,6 @@ static int mcam_vidioc_s_fmt_vid_cap(struct file *filp, void *priv, goto out; } mcam_set_config_needed(cam, 1); - ret = 0; out: mutex_unlock(&cam->s_mutex); return ret; diff --git a/drivers/media/video/mem2mem_testdev.c b/drivers/media/video/mem2mem_testdev.c index 12897e8a3314..d2dec585e61b 100644 --- a/drivers/media/video/mem2mem_testdev.c +++ b/drivers/media/video/mem2mem_testdev.c @@ -40,7 +40,7 @@ MODULE_VERSION("0.1.1"); #define MIN_H 32 #define MAX_W 640 #define MAX_H 480 -#define DIM_ALIGN_MASK 0x08 /* 8-alignment for dimensions */ +#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */ /* Flags that indicate a format can be used for capture/output */ #define MEM2MEM_CAPTURE (1 << 0) @@ -958,6 +958,10 @@ static int m2mtest_probe(struct platform_device *pdev) } *vfd = m2mtest_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->dev_mutex; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c index b09a3c80a15e..7bc775219f97 100644 --- a/drivers/media/video/meye.c +++ b/drivers/media/video/meye.c @@ -1570,7 +1570,7 @@ static long vidioc_default(struct file *file, void *fh, bool valid_prio, return meyeioc_stilljcapt((int *) arg); default: - return -EINVAL; + return -ENOTTY; } } diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c index 82ce50721de3..aeb22be7dcbd 100644 --- a/drivers/media/video/msp3400-driver.c +++ b/drivers/media/video/msp3400-driver.c @@ -597,19 +597,23 @@ static int msp_log_status(struct v4l2_subdev *sd) return 0; } -static int msp_suspend(struct i2c_client *client, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int msp_suspend(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); v4l_dbg(1, msp_debug, client, "suspend\n"); msp_reset(client); return 0; } -static int msp_resume(struct i2c_client *client) +static int msp_resume(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); v4l_dbg(1, msp_debug, client, "resume\n"); msp_wake_thread(client); return 0; } +#endif /* ----------------------------------------------------------------------- */ @@ -863,6 +867,10 @@ static int msp_remove(struct i2c_client *client) /* ----------------------------------------------------------------------- */ +static const struct dev_pm_ops msp3400_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(msp_suspend, msp_resume) +}; + static const struct i2c_device_id msp_id[] = { { "msp3400", 0 }, { } @@ -873,11 +881,10 @@ static struct i2c_driver msp_driver = { .driver = { .owner = THIS_MODULE, .name = "msp3400", + .pm = &msp3400_pm_ops, }, .probe = msp_probe, .remove = msp_remove, - .suspend = msp_suspend, - .resume = msp_resume, .id_table = msp_id, }; diff --git a/drivers/media/video/mt9m032.c b/drivers/media/video/mt9m032.c index 645973c5feb0..3c1e626139b7 100644 --- a/drivers/media/video/mt9m032.c +++ b/drivers/media/video/mt9m032.c @@ -838,9 +838,9 @@ static int mt9m032_remove(struct i2c_client *client) struct v4l2_subdev *subdev = i2c_get_clientdata(client); struct mt9m032 *sensor = to_mt9m032(subdev); - v4l2_device_unregister_subdev(&sensor->subdev); + v4l2_device_unregister_subdev(subdev); v4l2_ctrl_handler_free(&sensor->ctrls); - media_entity_cleanup(&sensor->subdev.entity); + media_entity_cleanup(&subdev->entity); mutex_destroy(&sensor->lock); kfree(sensor); return 0; diff --git a/drivers/media/video/mt9p031.c b/drivers/media/video/mt9p031.c index c81eaf4fbe01..8f061d9ac443 100644 --- a/drivers/media/video/mt9p031.c +++ b/drivers/media/video/mt9p031.c @@ -14,6 +14,7 @@ #include #include +#include #include #include #include @@ -90,7 +91,14 @@ #define MT9P031_GLOBAL_GAIN_MAX 1024 #define MT9P031_GLOBAL_GAIN_DEF 8 #define MT9P031_GLOBAL_GAIN_MULT (1 << 6) +#define MT9P031_ROW_BLACK_TARGET 0x49 #define MT9P031_ROW_BLACK_DEF_OFFSET 0x4b +#define MT9P031_GREEN1_OFFSET 0x60 +#define MT9P031_GREEN2_OFFSET 0x61 +#define MT9P031_BLACK_LEVEL_CALIBRATION 0x62 +#define MT9P031_BLC_MANUAL_BLC (1 << 0) +#define MT9P031_RED_OFFSET 0x63 +#define MT9P031_BLUE_OFFSET 0x64 #define MT9P031_TEST_PATTERN 0xa0 #define MT9P031_TEST_PATTERN_SHIFT 3 #define MT9P031_TEST_PATTERN_ENABLE (1 << 0) @@ -99,17 +107,27 @@ #define MT9P031_TEST_PATTERN_RED 0xa2 #define MT9P031_TEST_PATTERN_BLUE 0xa3 +enum mt9p031_model { + MT9P031_MODEL_COLOR, + MT9P031_MODEL_MONOCHROME, +}; + struct mt9p031 { struct v4l2_subdev subdev; struct media_pad pad; struct v4l2_rect crop; /* Sensor window */ struct v4l2_mbus_framefmt format; - struct v4l2_ctrl_handler ctrls; struct mt9p031_platform_data *pdata; struct mutex power_lock; /* lock to protect power_count */ int power_count; + enum mt9p031_model model; struct aptina_pll pll; + int reset; + + struct v4l2_ctrl_handler ctrls; + struct v4l2_ctrl *blc_auto; + struct v4l2_ctrl *blc_offset; /* Registers cache */ u16 output_control; @@ -241,8 +259,8 @@ static inline int mt9p031_pll_disable(struct mt9p031 *mt9p031) static int mt9p031_power_on(struct mt9p031 *mt9p031) { /* Ensure RESET_BAR is low */ - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 1); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); usleep_range(1000, 2000); } @@ -252,8 +270,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) mt9p031->pdata->ext_freq); /* Now RESET_BAR must be high */ - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 0); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 1); usleep_range(1000, 2000); } @@ -262,8 +280,8 @@ static int mt9p031_power_on(struct mt9p031 *mt9p031) static void mt9p031_power_off(struct mt9p031 *mt9p031) { - if (mt9p031->pdata->reset) { - mt9p031->pdata->reset(&mt9p031->subdev, 1); + if (mt9p031->reset != -1) { + gpio_set_value(mt9p031->reset, 0); usleep_range(1000, 2000); } @@ -557,6 +575,10 @@ static int mt9p031_set_crop(struct v4l2_subdev *subdev, */ #define V4L2_CID_TEST_PATTERN (V4L2_CID_USER_BASE | 0x1001) +#define V4L2_CID_BLC_AUTO (V4L2_CID_USER_BASE | 0x1002) +#define V4L2_CID_BLC_TARGET_LEVEL (V4L2_CID_USER_BASE | 0x1003) +#define V4L2_CID_BLC_ANALOG_OFFSET (V4L2_CID_USER_BASE | 0x1004) +#define V4L2_CID_BLC_DIGITAL_OFFSET (V4L2_CID_USER_BASE | 0x1005) static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) { @@ -621,11 +643,17 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_TEST_PATTERN: if (!ctrl->val) { - ret = mt9p031_set_mode2(mt9p031, - 0, MT9P031_READ_MODE_2_ROW_BLC); - if (ret < 0) - return ret; - + /* Restore the black level compensation settings. */ + if (mt9p031->blc_auto->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_auto); + if (ret < 0) + return ret; + } + if (mt9p031->blc_offset->cur.val != 0) { + ret = mt9p031_s_ctrl(mt9p031->blc_offset); + if (ret < 0) + return ret; + } return mt9p031_write(client, MT9P031_TEST_PATTERN, MT9P031_TEST_PATTERN_DISABLE); } @@ -640,10 +668,14 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) if (ret < 0) return ret; + /* Disable digital black level compensation when using a test + * pattern. + */ ret = mt9p031_set_mode2(mt9p031, MT9P031_READ_MODE_2_ROW_BLC, 0); if (ret < 0) return ret; + ret = mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, 0); if (ret < 0) return ret; @@ -651,7 +683,40 @@ static int mt9p031_s_ctrl(struct v4l2_ctrl *ctrl) return mt9p031_write(client, MT9P031_TEST_PATTERN, ((ctrl->val - 1) << MT9P031_TEST_PATTERN_SHIFT) | MT9P031_TEST_PATTERN_ENABLE); + + case V4L2_CID_BLC_AUTO: + ret = mt9p031_set_mode2(mt9p031, + ctrl->val ? 0 : MT9P031_READ_MODE_2_ROW_BLC, + ctrl->val ? MT9P031_READ_MODE_2_ROW_BLC : 0); + if (ret < 0) + return ret; + + return mt9p031_write(client, MT9P031_BLACK_LEVEL_CALIBRATION, + ctrl->val ? 0 : MT9P031_BLC_MANUAL_BLC); + + case V4L2_CID_BLC_TARGET_LEVEL: + return mt9p031_write(client, MT9P031_ROW_BLACK_TARGET, + ctrl->val); + + case V4L2_CID_BLC_ANALOG_OFFSET: + data = ctrl->val & ((1 << 9) - 1); + + ret = mt9p031_write(client, MT9P031_GREEN1_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_GREEN2_OFFSET, data); + if (ret < 0) + return ret; + ret = mt9p031_write(client, MT9P031_RED_OFFSET, data); + if (ret < 0) + return ret; + return mt9p031_write(client, MT9P031_BLUE_OFFSET, data); + + case V4L2_CID_BLC_DIGITAL_OFFSET: + return mt9p031_write(client, MT9P031_ROW_BLACK_DEF_OFFSET, + ctrl->val & ((1 << 12) - 1)); } + return 0; } @@ -685,6 +750,46 @@ static const struct v4l2_ctrl_config mt9p031_ctrls[] = { .flags = 0, .menu_skip_mask = 0, .qmenu = mt9p031_test_pattern_menu, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_AUTO, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "BLC, Auto", + .min = 0, + .max = 1, + .step = 1, + .def = 1, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_TARGET_LEVEL, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Target Level", + .min = 0, + .max = 4095, + .step = 1, + .def = 168, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_ANALOG_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Analog Offset", + .min = -255, + .max = 255, + .step = 1, + .def = 32, + .flags = 0, + }, { + .ops = &mt9p031_ctrl_ops, + .id = V4L2_CID_BLC_DIGITAL_OFFSET, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "BLC Digital Offset", + .min = -2048, + .max = 2047, + .step = 1, + .def = 40, + .flags = 0, } }; @@ -764,7 +869,7 @@ static int mt9p031_open(struct v4l2_subdev *subdev, struct v4l2_subdev_fh *fh) format = v4l2_subdev_get_try_format(fh, 0); - if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION) + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) format->code = V4L2_MBUS_FMT_Y12_1X12; else format->code = V4L2_MBUS_FMT_SGRBG12_1X12; @@ -842,6 +947,8 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->pdata = pdata; mt9p031->output_control = MT9P031_OUTPUT_CONTROL_DEF; mt9p031->mode2 = MT9P031_READ_MODE_2_ROW_BLC; + mt9p031->model = did->driver_data; + mt9p031->reset = -1; v4l2_ctrl_handler_init(&mt9p031->ctrls, ARRAY_SIZE(mt9p031_ctrls) + 4); @@ -862,9 +969,16 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->subdev.ctrl_handler = &mt9p031->ctrls; - if (mt9p031->ctrls.error) + if (mt9p031->ctrls.error) { printk(KERN_INFO "%s: control initialization error %d\n", __func__, mt9p031->ctrls.error); + ret = mt9p031->ctrls.error; + goto done; + } + + mt9p031->blc_auto = v4l2_ctrl_find(&mt9p031->ctrls, V4L2_CID_BLC_AUTO); + mt9p031->blc_offset = v4l2_ctrl_find(&mt9p031->ctrls, + V4L2_CID_BLC_DIGITAL_OFFSET); mutex_init(&mt9p031->power_lock); v4l2_i2c_subdev_init(&mt9p031->subdev, client, &mt9p031_subdev_ops); @@ -882,7 +996,7 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->crop.left = MT9P031_COLUMN_START_DEF; mt9p031->crop.top = MT9P031_ROW_START_DEF; - if (mt9p031->pdata->version == MT9P031_MONOCHROME_VERSION) + if (mt9p031->model == MT9P031_MODEL_MONOCHROME) mt9p031->format.code = V4L2_MBUS_FMT_Y12_1X12; else mt9p031->format.code = V4L2_MBUS_FMT_SGRBG12_1X12; @@ -892,10 +1006,22 @@ static int mt9p031_probe(struct i2c_client *client, mt9p031->format.field = V4L2_FIELD_NONE; mt9p031->format.colorspace = V4L2_COLORSPACE_SRGB; + if (pdata->reset != -1) { + ret = gpio_request_one(pdata->reset, GPIOF_OUT_INIT_LOW, + "mt9p031_rst"); + if (ret < 0) + goto done; + + mt9p031->reset = pdata->reset; + } + ret = mt9p031_pll_setup(mt9p031); done: if (ret < 0) { + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); + v4l2_ctrl_handler_free(&mt9p031->ctrls); media_entity_cleanup(&mt9p031->subdev.entity); kfree(mt9p031); @@ -912,13 +1038,16 @@ static int mt9p031_remove(struct i2c_client *client) v4l2_ctrl_handler_free(&mt9p031->ctrls); v4l2_device_unregister_subdev(subdev); media_entity_cleanup(&subdev->entity); + if (mt9p031->reset != -1) + gpio_free(mt9p031->reset); kfree(mt9p031); return 0; } static const struct i2c_device_id mt9p031_id[] = { - { "mt9p031", 0 }, + { "mt9p031", MT9P031_MODEL_COLOR }, + { "mt9p031m", MT9P031_MODEL_MONOCHROME }, { } }; MODULE_DEVICE_TABLE(i2c, mt9p031_id); diff --git a/drivers/media/video/mt9t112.c b/drivers/media/video/mt9t112.c index 8d1445f12708..e1ae46a7ee96 100644 --- a/drivers/media/video/mt9t112.c +++ b/drivers/media/video/mt9t112.c @@ -453,6 +453,7 @@ static int mt9t112_init_pll(const struct i2c_client *client) * I2C Master Clock Divider */ mt9t112_reg_write(ret, client, 0x0014, 0x3046); + mt9t112_reg_write(ret, client, 0x0016, 0x0400); /* JPEG initialization workaround */ mt9t112_reg_write(ret, client, 0x0022, 0x0190); mt9t112_reg_write(ret, client, 0x3B84, 0x0212); diff --git a/drivers/media/video/mt9v032.c b/drivers/media/video/mt9v032.c index 75e253a343c5..4ba4884c016e 100644 --- a/drivers/media/video/mt9v032.c +++ b/drivers/media/video/mt9v032.c @@ -481,7 +481,7 @@ static int mt9v032_s_ctrl(struct v4l2_ctrl *ctrl) case V4L2_CID_EXPOSURE_AUTO: return mt9v032_update_aec_agc(mt9v032, MT9V032_AEC_ENABLE, - ctrl->val); + !ctrl->val); case V4L2_CID_EXPOSURE: return mt9v032_write(client, MT9V032_TOTAL_SHUTTER_WIDTH, diff --git a/drivers/media/video/mx1_camera.c b/drivers/media/video/mx1_camera.c index 055d11ddb038..4296a8350298 100644 --- a/drivers/media/video/mx1_camera.c +++ b/drivers/media/video/mx1_camera.c @@ -126,13 +126,8 @@ static int mx1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytes_per_line < 0) - return bytes_per_line; - - *size = bytes_per_line * icd->user_height; + *size = icd->sizeimage; if (!*count) *count = 32; @@ -171,11 +166,6 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, struct soc_camera_device *icd = vq->priv_data; struct mx1_buffer *buf = container_of(vb, struct mx1_buffer, vb); int ret; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; dev_dbg(icd->parent, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -202,7 +192,7 @@ static int mx1_videobuf_prepare(struct videobuf_queue *vq, vb->state = VIDEOBUF_NEEDS_INIT; } - vb->size = bytes_per_line * vb->height; + vb->size = icd->sizeimage; if (0 != vb->baddr && vb->bsize < vb->size) { ret = -EINVAL; goto out; diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 18afaeeadb7b..ded26b7286fa 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -344,6 +345,19 @@ static struct mx2_fmt_cfg mx27_emma_prp_table[] = { PRP_INTR_CH2OVF, } }, + { + .in_fmt = V4L2_MBUS_FMT_UYVY8_2X8, + .out_fmt = V4L2_PIX_FMT_YUV420, + .cfg = { + .channel = 2, + .in_fmt = PRP_CNTL_DATA_IN_YUV422, + .out_fmt = PRP_CNTL_CH2_OUT_YUV420, + .src_pixel = 0x22000888, /* YUV422 (YUYV) */ + .irq_flags = PRP_INTR_RDERR | PRP_INTR_CH2WERR | + PRP_INTR_CH2FC | PRP_INTR_LBOVF | + PRP_INTR_CH2OVF, + } + }, }; static struct mx2_fmt_cfg *mx27_emma_prp_get_format( @@ -525,8 +539,6 @@ static int mx2_videobuf_setup(struct vb2_queue *vq, struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); dev_dbg(icd->parent, "count=%d, size=%d\n", *count, sizes[0]); @@ -534,12 +546,9 @@ static int mx2_videobuf_setup(struct vb2_queue *vq, if (fmt != NULL) return -ENOTTY; - if (bytes_per_line < 0) - return bytes_per_line; - alloc_ctxs[0] = pcdev->alloc_ctx; - sizes[0] = bytes_per_line * icd->user_height; + sizes[0] = icd->sizeimage; if (0 == *count) *count = 32; @@ -555,16 +564,11 @@ static int mx2_videobuf_setup(struct vb2_queue *vq, static int mx2_videobuf_prepare(struct vb2_buffer *vb) { struct soc_camera_device *icd = soc_camera_from_vb2q(vb->vb2_queue); - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); int ret = 0; dev_dbg(icd->parent, "%s (vb=0x%p) 0x%p %lu\n", __func__, vb, vb2_plane_vaddr(vb, 0), vb2_get_plane_payload(vb, 0)); - if (bytes_per_line < 0) - return bytes_per_line; - #ifdef DEBUG /* * This can be useful if you want to see if we actually fill @@ -574,7 +578,7 @@ static int mx2_videobuf_prepare(struct vb2_buffer *vb) 0xaa, vb2_get_plane_payload(vb, 0)); #endif - vb2_set_plane_payload(vb, 0, bytes_per_line * icd->user_height); + vb2_set_plane_payload(vb, 0, icd->sizeimage); if (vb2_plane_vaddr(vb, 0) && vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) { ret = -EINVAL; @@ -980,6 +984,7 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx2_camera_dev *pcdev = ici->priv; struct v4l2_mbus_config cfg = {.type = V4L2_MBUS_PARALLEL,}; + const struct soc_camera_format_xlate *xlate; unsigned long common_flags; int ret; int bytesperline; @@ -1024,14 +1029,31 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) return ret; } + xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); + if (!xlate) { + dev_warn(icd->parent, "Format %x not found\n", pixfmt); + return -EINVAL; + } + + if (xlate->code == V4L2_MBUS_FMT_YUYV8_2X8) { + csicr1 |= CSICR1_PACK_DIR; + csicr1 &= ~CSICR1_SWAP16_EN; + dev_dbg(icd->parent, "already yuyv format, don't convert\n"); + } else if (xlate->code == V4L2_MBUS_FMT_UYVY8_2X8) { + csicr1 &= ~CSICR1_PACK_DIR; + csicr1 |= CSICR1_SWAP16_EN; + dev_dbg(icd->parent, "convert uyvy mbus format into yuyv\n"); + } else { + dev_warn(icd->parent, "mbus format not supported\n"); + return -EINVAL; + } + if (common_flags & V4L2_MBUS_PCLK_SAMPLE_RISING) csicr1 |= CSICR1_REDGE; if (common_flags & V4L2_MBUS_VSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_SOF_POL; if (common_flags & V4L2_MBUS_HSYNC_ACTIVE_HIGH) csicr1 |= CSICR1_HSYNC_POL; - if (pcdev->platform_flags & MX2_CAMERA_SWAP16) - csicr1 |= CSICR1_SWAP16_EN; if (pcdev->platform_flags & MX2_CAMERA_EXT_VSYNC) csicr1 |= CSICR1_EXT_VSYNC; if (pcdev->platform_flags & MX2_CAMERA_CCIR) @@ -1042,8 +1064,6 @@ static int mx2_camera_set_bus_param(struct soc_camera_device *icd) csicr1 |= CSICR1_GCLK_MODE; if (pcdev->platform_flags & MX2_CAMERA_INV_DATA) csicr1 |= CSICR1_INV_DATA; - if (pcdev->platform_flags & MX2_CAMERA_PACK_DIR_MSB) - csicr1 |= CSICR1_PACK_DIR; pcdev->csicr1 = csicr1; @@ -1118,7 +1138,8 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, return 0; } - if (code == V4L2_MBUS_FMT_YUYV8_2X8) { + if (code == V4L2_MBUS_FMT_YUYV8_2X8 || + code == V4L2_MBUS_FMT_UYVY8_2X8) { formats++; if (xlate) { /* @@ -1134,6 +1155,18 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, } } + if (code == V4L2_MBUS_FMT_UYVY8_2X8) { + formats++; + if (xlate) { + xlate->host_fmt = + soc_mbus_get_fmtdesc(V4L2_MBUS_FMT_YUYV8_2X8); + xlate->code = code; + dev_dbg(dev, "Providing host format %s for sensor code %d\n", + xlate->host_fmt->name, code); + xlate++; + } + } + /* Generic pass-trough */ formats++; if (xlate) { @@ -1363,17 +1396,20 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, xlate->host_fmt); if (pix->bytesperline < 0) return pix->bytesperline; - pix->sizeimage = pix->height * pix->bytesperline; + pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, + pix->bytesperline, pix->height); /* Check against the CSIRXCNT limit */ if (pix->sizeimage > 4 * 0x3ffff) { /* Adjust geometry, preserve aspect ratio */ - unsigned int new_height = int_sqrt(4 * 0x3ffff * - pix->height / pix->bytesperline); + unsigned int new_height = int_sqrt(div_u64(0x3ffffULL * + 4 * pix->height, pix->bytesperline)); pix->width = new_height * pix->width / pix->height; pix->height = new_height; pix->bytesperline = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); BUG_ON(pix->bytesperline < 0); + pix->sizeimage = soc_mbus_image_size(xlate->host_fmt, + pix->bytesperline, pix->height); } } @@ -1752,6 +1788,8 @@ static int __devinit mx2_camera_probe(struct platform_device *pdev) pcdev->soc_host.priv = pcdev; pcdev->soc_host.v4l2_dev.dev = &pdev->dev; pcdev->soc_host.nr = pdev->id; + if (cpu_is_mx25()) + pcdev->soc_host.capabilities = SOCAM_HOST_CAP_STRIDE; pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(pcdev->alloc_ctx)) { diff --git a/drivers/media/video/mx2_emmaprp.c b/drivers/media/video/mx2_emmaprp.c index ba89a7401c8c..0bd5815de369 100644 --- a/drivers/media/video/mx2_emmaprp.c +++ b/drivers/media/video/mx2_emmaprp.c @@ -755,7 +755,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(src_vq, 0, sizeof(*src_vq)); src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT; - src_vq->io_modes = VB2_MMAP; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; src_vq->drv_priv = ctx; src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); src_vq->ops = &emmaprp_qops; @@ -767,7 +767,7 @@ static int queue_init(void *priv, struct vb2_queue *src_vq, memset(dst_vq, 0, sizeof(*dst_vq)); dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - dst_vq->io_modes = VB2_MMAP; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; dst_vq->drv_priv = ctx; dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); dst_vq->ops = &emmaprp_qops; @@ -904,6 +904,10 @@ static int emmaprp_probe(struct platform_device *pdev) } *vfd = emmaprp_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &pcdev->dev_mutex; video_set_drvdata(vfd, pcdev); diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c index 93c35ef5f0ad..02d54a057b60 100644 --- a/drivers/media/video/mx3_camera.c +++ b/drivers/media/video/mx3_camera.c @@ -199,8 +199,6 @@ static int mx3_videobuf_setup(struct vb2_queue *vq, struct soc_camera_device *icd = soc_camera_from_vb2q(vq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct mx3_camera_dev *mx3_cam = ici->priv; - int bytes_per_line; - unsigned int height; if (!mx3_cam->idmac_channel[0]) return -EINVAL; @@ -208,21 +206,29 @@ static int mx3_videobuf_setup(struct vb2_queue *vq, if (fmt) { const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd, fmt->fmt.pix.pixelformat); + unsigned int bytes_per_line; + int ret; + if (!xlate) return -EINVAL; - bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width, - xlate->host_fmt); - height = fmt->fmt.pix.height; + + ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width, + xlate->host_fmt); + if (ret < 0) + return ret; + + bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line, + fmt->fmt.pix.height); + if (ret < 0) + return ret; + + sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret); } else { /* Called from VIDIOC_REQBUFS or in compatibility mode */ - bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - height = icd->user_height; + sizes[0] = icd->sizeimage; } - if (bytes_per_line < 0) - return bytes_per_line; - - sizes[0] = bytes_per_line * height; alloc_ctxs[0] = mx3_cam->alloc_ctx; @@ -267,14 +273,11 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) struct idmac_channel *ichan = mx3_cam->idmac_channel[0]; struct idmac_video_param *video = &ichan->params.video; const struct soc_mbus_pixelfmt *host_fmt = icd->current_fmt->host_fmt; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, host_fmt); unsigned long flags; dma_cookie_t cookie; size_t new_size; - BUG_ON(bytes_per_line <= 0); - - new_size = bytes_per_line * icd->user_height; + new_size = icd->sizeimage; if (vb2_plane_size(vb, 0) < new_size) { dev_err(icd->parent, "Buffer #%d too small (%lu < %zu)\n", @@ -314,9 +317,9 @@ static void mx3_videobuf_queue(struct vb2_buffer *vb) * horizontal parameters in this case are expressed in bytes, * not in pixels. */ - video->out_width = bytes_per_line; + video->out_width = icd->bytesperline; video->out_height = icd->user_height; - video->out_stride = bytes_per_line; + video->out_stride = icd->bytesperline; } else { /* * For IPU known formats the pixel unit will be managed @@ -642,12 +645,14 @@ static const struct soc_mbus_pixelfmt mx3_camera_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, { .fourcc = V4L2_PIX_FMT_GREY, .name = "Monochrome 8 bit", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }; diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c index 2e4131748438..b520a45cb3f3 100644 --- a/drivers/media/video/mxb.c +++ b/drivers/media/video/mxb.c @@ -31,10 +31,11 @@ #include #include -#include "mxb.h" #include "tea6415c.h" #include "tea6420.h" +#define MXB_AUDIOS 6 + #define I2C_SAA7111A 0x24 #define I2C_TDA9840 0x42 #define I2C_TEA6415C 0x43 @@ -62,10 +63,14 @@ MODULE_PARM_DESC(debug, "Turn on/off device debugging (default:off)."); enum { TUNER, AUX1, AUX3, AUX3_YC }; static struct v4l2_input mxb_inputs[MXB_INPUTS] = { - { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, - { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 4, 0, V4L2_STD_PAL_BG|V4L2_STD_NTSC_M, 0, V4L2_IN_CAP_STD }, + { TUNER, "Tuner", V4L2_INPUT_TYPE_TUNER, 0x3f, 0, + V4L2_STD_PAL_BG | V4L2_STD_PAL_I, 0, V4L2_IN_CAP_STD }, + { AUX1, "AUX1", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3, "AUX3 Composite", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, + { AUX3_YC, "AUX3 S-Video", V4L2_INPUT_TYPE_CAMERA, 0x3f, 0, + V4L2_STD_ALL, 0, V4L2_IN_CAP_STD }, }; /* this array holds the information, which port of the saa7146 each @@ -90,6 +95,36 @@ struct mxb_routing { u32 output; }; +/* these are the available audio sources, which can switched + to the line- and cd-output individually */ +static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { + { + .index = 0, + .name = "Tuner", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 1, + .name = "AUX1", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 2, + .name = "AUX2", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 3, + .name = "AUX3", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 4, + .name = "Radio (X9)", + .capability = V4L2_AUDCAP_STEREO, + } , { + .index = 5, + .name = "CD-ROM (X10)", + .capability = V4L2_AUDCAP_STEREO, + } +}; + /* These are the necessary input-output-pins for bringing one audio source (see above) to the CD-output. Note that gain is set to 0 in this table. */ static struct mxb_routing TEA6420_cd[MXB_AUDIOS + 1][2] = { @@ -114,11 +149,6 @@ static struct mxb_routing TEA6420_line[MXB_AUDIOS + 1][2] = { { { 6, 3 }, { 6, 2 } } /* Mute */ }; -#define MAXCONTROLS 1 -static struct v4l2_queryctrl mxb_controls[] = { - { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 }, -}; - struct mxb { struct video_device *video_dev; @@ -135,6 +165,7 @@ struct mxb int cur_mode; /* current audio mode (mono, stereo, ...) */ int cur_input; /* current input */ + int cur_audinput; /* current audio input */ int cur_mute; /* current mute status */ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */ }; @@ -150,16 +181,21 @@ struct mxb #define call_all(dev, o, f, args...) \ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args) -static inline void tea6420_route_cd(struct mxb *mxb, int idx) +static void mxb_update_audmode(struct mxb *mxb) +{ + struct v4l2_tuner t = { + .audmode = mxb->cur_mode, + }; + + tda9840_call(mxb, tuner, s_tuner, &t); +} + +static inline void tea6420_route(struct mxb *mxb, int idx) { v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, TEA6420_cd[idx][0].input, TEA6420_cd[idx][0].output, 0); v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, TEA6420_cd[idx][1].input, TEA6420_cd[idx][1].output, 0); -} - -static inline void tea6420_route_line(struct mxb *mxb, int idx) -{ v4l2_subdev_call(mxb->tea6420_1, audio, s_routing, TEA6420_line[idx][0].input, TEA6420_line[idx][0].output, 0); v4l2_subdev_call(mxb->tea6420_2, audio, s_routing, @@ -168,16 +204,45 @@ static inline void tea6420_route_line(struct mxb *mxb, int idx) static struct saa7146_extension extension; +static int mxb_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct saa7146_dev *dev = container_of(ctrl->handler, + struct saa7146_dev, ctrl_handler); + struct mxb *mxb = dev->ext_priv; + + switch (ctrl->id) { + case V4L2_CID_AUDIO_MUTE: + mxb->cur_mute = ctrl->val; + /* switch the audio-source */ + tea6420_route(mxb, ctrl->val ? 6 : + video_audio_connect[mxb->cur_input]); + break; + default: + return -EINVAL; + } + return 0; +} + +static const struct v4l2_ctrl_ops mxb_ctrl_ops = { + .s_ctrl = mxb_s_ctrl, +}; + static int mxb_probe(struct saa7146_dev *dev) { + struct v4l2_ctrl_handler *hdl = &dev->ctrl_handler; struct mxb *mxb = NULL; + v4l2_ctrl_new_std(hdl, &mxb_ctrl_ops, + V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1); + if (hdl->error) + return hdl->error; mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL); if (mxb == NULL) { DEB_D("not enough kernel memory\n"); return -ENOMEM; } + snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num); saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480); @@ -214,6 +279,8 @@ static int mxb_probe(struct saa7146_dev *dev) /* we store the pointer in our private data field */ dev->ext_priv = mxb; + v4l2_ctrl_handler_setup(hdl); + return 0; } @@ -286,6 +353,9 @@ static int mxb_init_done(struct saa7146_dev* dev) int i = 0, err = 0; + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + /* select video mode in saa7111a */ saa7111a_call(mxb, core, s_std, std); @@ -306,12 +376,12 @@ static int mxb_init_done(struct saa7146_dev* dev) tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); /* set a default video standard */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ + saa7146_write(dev, GPIO_CTRL, 0x00404050); + saa7111a_call(mxb, core, s_gpio, 1); + saa7111a_call(mxb, core, s_std, std); tuner_call(mxb, core, s_std, std); - /* mute audio on tea6420s */ - tea6420_route_line(mxb, 6); - tea6420_route_cd(mxb, 6); - /* switch to tuner-channel on tea6415c */ tea6415c_call(mxb, video, s_routing, 3, 17, 0); @@ -320,9 +390,11 @@ static int mxb_init_done(struct saa7146_dev* dev) /* the rest for mxb */ mxb->cur_input = 0; + mxb->cur_audinput = video_audio_connect[mxb->cur_input]; mxb->cur_mute = 1; mxb->cur_mode = V4L2_TUNER_MODE_STEREO; + mxb_update_audmode(mxb); /* check if the saa7740 (aka 'sound arena module') is present on the mxb. if so, we must initialize it. due to lack of @@ -385,69 +457,6 @@ void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask) } */ -static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - int i; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == qc->id) { - *qc = mxb_controls[i]; - DEB_D("VIDIOC_QUERYCTRL %d\n", qc->id); - return 0; - } - } - return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc); -} - -static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - int i; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_AUDIO_MUTE) { - vc->value = mxb->cur_mute; - DEB_D("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value); - return 0; - } - - DEB_EE("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d\n", vc->value); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - int i = 0; - - for (i = MAXCONTROLS - 1; i >= 0; i--) { - if (mxb_controls[i].id == vc->id) - break; - } - - if (i < 0) - return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc); - - if (vc->id == V4L2_CID_AUDIO_MUTE) { - mxb->cur_mute = vc->value; - /* switch the audio-source */ - tea6420_route_line(mxb, vc->value ? 6 : - video_audio_connect[mxb->cur_input]); - DEB_EE("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d\n", vc->value); - } - return 0; -} - static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i) { DEB_EE("VIDIOC_ENUMINPUT %d\n", i->index); @@ -519,9 +528,12 @@ static int vidioc_s_input(struct file *file, void *fh, unsigned int input) if (saa7111a_call(mxb, video, s_routing, i, SAA7111_FMT_CCIR, 0)) pr_err("VIDIOC_S_INPUT: could not address saa7111a\n"); + mxb->cur_audinput = video_audio_connect[input]; /* switch the audio-source only if necessary */ if (0 == mxb->cur_mute) - tea6420_route_line(mxb, video_audio_connect[input]); + tea6420_route(mxb, mxb->cur_audinput); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); return 0; } @@ -563,17 +575,20 @@ static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t) return call_all(dev, tuner, s_tuner, t); } +static int vidioc_querystd(struct file *file, void *fh, v4l2_std_id *norm) +{ + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + + return call_all(dev, video, querystd, norm); +} + static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - if (mxb->cur_input) { - DEB_D("VIDIOC_G_FREQ: channel %d does not have a tuner!\n", - mxb->cur_input); + if (f->tuner) return -EINVAL; - } - *f = mxb->cur_freq; DEB_EE("VIDIOC_G_FREQ: freq:0x%08x\n", mxb->cur_freq.frequency); @@ -592,17 +607,18 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency if (V4L2_TUNER_ANALOG_TV != f->type) return -EINVAL; - if (mxb->cur_input) { - DEB_D("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", - mxb->cur_input); - return -EINVAL; - } - - mxb->cur_freq = *f; DEB_EE("VIDIOC_S_FREQUENCY: freq:0x%08x\n", mxb->cur_freq.frequency); /* tune in desired frequency */ - tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq); + tuner_call(mxb, tuner, s_frequency, f); + /* let the tuner subdev clamp the frequency to the tuner range */ + tuner_call(mxb, tuner, g_frequency, f); + mxb->cur_freq = *f; + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + + if (mxb->cur_input) + return 0; /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */ spin_lock(&dev->slock); @@ -612,25 +628,40 @@ static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency return 0; } +static int vidioc_enumaudio(struct file *file, void *fh, struct v4l2_audio *a) +{ + if (a->index >= MXB_AUDIOS) + return -EINVAL; + *a = mxb_audios[a->index]; + return 0; +} + static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a) { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; struct mxb *mxb = (struct mxb *)dev->ext_priv; - if (a->index > MXB_INPUTS) { - DEB_D("VIDIOC_G_AUDIO %d out of range\n", a->index); - return -EINVAL; - } - - DEB_EE("VIDIOC_G_AUDIO %d\n", a->index); - memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio)); + DEB_EE("VIDIOC_G_AUDIO\n"); + *a = mxb_audios[mxb->cur_audinput]; return 0; } static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a) { + struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + struct mxb *mxb = (struct mxb *)dev->ext_priv; + DEB_D("VIDIOC_S_AUDIO %d\n", a->index); - return 0; + if (mxb_inputs[mxb->cur_input].audioset & (1 << a->index)) { + if (mxb->cur_audinput != a->index) { + mxb->cur_audinput = a->index; + tea6420_route(mxb, a->index); + if (mxb->cur_audinput == 0) + mxb_update_audmode(mxb); + } + return 0; + } + return -EINVAL; } #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -638,61 +669,32 @@ static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_regist { struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - return call_all(dev, core, g_register, reg); + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (v4l2_chip_match_host(®->match)) { + reg->val = saa7146_read(dev, reg->reg); + reg->size = 4; + return 0; + } + 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 saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (v4l2_chip_match_host(®->match)) { + saa7146_write(dev, reg->reg, reg->val); + reg->size = 4; + return 0; + } return call_all(dev, core, s_register, reg); } #endif -static long vidioc_default(struct file *file, void *fh, bool valid_prio, - int cmd, void *arg) -{ - struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev; - struct mxb *mxb = (struct mxb *)dev->ext_priv; - - switch (cmd) { - case MXB_S_AUDIO_CD: - { - int i = *(int *)arg; - - if (i < 0 || i >= MXB_AUDIOS) { - DEB_D("invalid argument to MXB_S_AUDIO_CD: i:%d\n", i); - return -EINVAL; - } - - DEB_EE("MXB_S_AUDIO_CD: i:%d\n", i); - - tea6420_route_cd(mxb, i); - return 0; - } - case MXB_S_AUDIO_LINE: - { - int i = *(int *)arg; - - if (i < 0 || i >= MXB_AUDIOS) { - DEB_D("invalid argument to MXB_S_AUDIO_LINE: i:%d\n", - i); - return -EINVAL; - } - - DEB_EE("MXB_S_AUDIO_LINE: i:%d\n", i); - tea6420_route_line(mxb, i); - return 0; - } - default: -/* - DEB2(pr_err("does not handle this ioctl\n")); -*/ - return -ENOIOCTLCMD; - } - return 0; -} - static struct saa7146_ext_vv vv_data; /* this function only gets called when the probing was successful */ @@ -709,23 +711,21 @@ static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data } mxb = (struct mxb *)dev->ext_priv; - vv_data.ops.vidioc_queryctrl = vidioc_queryctrl; - vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl; - vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl; - vv_data.ops.vidioc_enum_input = vidioc_enum_input; - vv_data.ops.vidioc_g_input = vidioc_g_input; - vv_data.ops.vidioc_s_input = vidioc_s_input; - vv_data.ops.vidioc_g_tuner = vidioc_g_tuner; - vv_data.ops.vidioc_s_tuner = vidioc_s_tuner; - vv_data.ops.vidioc_g_frequency = vidioc_g_frequency; - vv_data.ops.vidioc_s_frequency = vidioc_s_frequency; - vv_data.ops.vidioc_g_audio = vidioc_g_audio; - vv_data.ops.vidioc_s_audio = vidioc_s_audio; + vv_data.vid_ops.vidioc_enum_input = vidioc_enum_input; + vv_data.vid_ops.vidioc_g_input = vidioc_g_input; + vv_data.vid_ops.vidioc_s_input = vidioc_s_input; + vv_data.vid_ops.vidioc_querystd = vidioc_querystd; + vv_data.vid_ops.vidioc_g_tuner = vidioc_g_tuner; + vv_data.vid_ops.vidioc_s_tuner = vidioc_s_tuner; + vv_data.vid_ops.vidioc_g_frequency = vidioc_g_frequency; + vv_data.vid_ops.vidioc_s_frequency = vidioc_s_frequency; + vv_data.vid_ops.vidioc_enumaudio = vidioc_enumaudio; + vv_data.vid_ops.vidioc_g_audio = vidioc_g_audio; + vv_data.vid_ops.vidioc_s_audio = vidioc_s_audio; #ifdef CONFIG_VIDEO_ADV_DEBUG - vv_data.ops.vidioc_g_register = vidioc_g_register; - vv_data.ops.vidioc_s_register = vidioc_s_register; + vv_data.vid_ops.vidioc_g_register = vidioc_g_register; + vv_data.vid_ops.vidioc_s_register = vidioc_s_register; #endif - vv_data.ops.vidioc_default = vidioc_default; if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) { ERR("cannot register capture v4l2 device. skipping.\n"); saa7146_vv_release(dev); @@ -752,6 +752,9 @@ static int mxb_detach(struct saa7146_dev *dev) DEB_EE("dev:%p\n", dev); + /* mute audio on tea6420s */ + tea6420_route(mxb, 6); + saa7146_unregister_device(&mxb->video_dev,dev); if (MXB_BOARD_CAN_DO_VBI(dev)) saa7146_unregister_device(&mxb->vbi_dev, dev); @@ -773,20 +776,24 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa v4l2_std_id std = V4L2_STD_PAL_I; DEB_D("VIDIOC_S_STD: setting mxb for PAL_I\n"); - /* set the 7146 gpio register -- I don't know what this does exactly */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); - /* unset the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 0); - tuner_call(mxb, core, s_std, std); + saa7111a_call(mxb, core, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, core, s_std, std); } else { v4l2_std_id std = V4L2_STD_PAL_BG; + if (mxb->cur_input) + std = standard->id; DEB_D("VIDIOC_S_STD: setting mxb for PAL/NTSC/SECAM\n"); - /* set the 7146 gpio register -- I don't know what this does exactly */ + /* These two gpio calls set the GPIO pins that control the tda9820 */ saa7146_write(dev, GPIO_CTRL, 0x00404050); - /* set the 7111 gpio register -- I don't know what this does exactly */ saa7111a_call(mxb, core, s_gpio, 1); - tuner_call(mxb, core, s_std, std); + saa7111a_call(mxb, core, s_std, std); + if (mxb->cur_input == 0) + tuner_call(mxb, core, s_std, std); } return 0; } @@ -836,14 +843,14 @@ MODULE_DEVICE_TABLE(pci, pci_tbl); static struct saa7146_ext_vv vv_data = { .inputs = MXB_INPUTS, - .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE, + .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_AUDIO, .stds = &standard[0], .num_stds = sizeof(standard)/sizeof(struct saa7146_standard), .std_callback = &std_callback, }; static struct saa7146_extension extension = { - .name = MXB_IDENTIFIER, + .name = "Multimedia eXtension Board", .flags = SAA7146_USE_I2C_IRQ, .pci_tbl = &pci_tbl[0], diff --git a/drivers/media/video/mxb.h b/drivers/media/video/mxb.h deleted file mode 100644 index 400a57ba62ec..000000000000 --- a/drivers/media/video/mxb.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef __MXB__ -#define __MXB__ - -#define BASE_VIDIOC_MXB 10 - -#define MXB_S_AUDIO_CD _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+0, int) -#define MXB_S_AUDIO_LINE _IOW ('V', BASE_VIDIOC_PRIVATE+BASE_VIDIOC_MXB+1, int) - -#define MXB_IDENTIFIER "Multimedia eXtension Board" - -#define MXB_AUDIOS 6 - -/* these are the available audio sources, which can switched - to the line- and cd-output individually */ -static struct v4l2_audio mxb_audios[MXB_AUDIOS] = { - { - .index = 0, - .name = "Tuner", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 1, - .name = "AUX1", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 2, - .name = "AUX2", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 3, - .name = "AUX3", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 4, - .name = "Radio (X9)", - .capability = V4L2_AUDCAP_STEREO, - } , { - .index = 5, - .name = "CD-ROM (X10)", - .capability = V4L2_AUDCAP_STEREO, - } -}; -#endif diff --git a/drivers/media/video/omap1_camera.c b/drivers/media/video/omap1_camera.c index c20f5ecd6790..c7e41145041f 100644 --- a/drivers/media/video/omap1_camera.c +++ b/drivers/media/video/omap1_camera.c @@ -206,15 +206,10 @@ static int omap1_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; - if (bytes_per_line < 0) - return bytes_per_line; - - *size = bytes_per_line * icd->user_height; + *size = icd->sizeimage; if (!*count || *count < OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode)) *count = OMAP1_CAMERA_MIN_BUF_COUNT(pcdev->vb_mode); @@ -256,15 +251,10 @@ static int omap1_videobuf_prepare(struct videobuf_queue *vq, { struct soc_camera_device *icd = vq->priv_data; struct omap1_cam_buf *buf = container_of(vb, struct omap1_cam_buf, vb); - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct omap1_cam_dev *pcdev = ici->priv; int ret; - if (bytes_per_line < 0) - return bytes_per_line; - WARN_ON(!list_empty(&vb->queue)); BUG_ON(NULL == icd->current_fmt); @@ -281,7 +271,7 @@ static int omap1_videobuf_prepare(struct videobuf_queue *vq, vb->state = VIDEOBUF_NEEDS_INIT; } - vb->size = bytes_per_line * vb->height; + vb->size = icd->sizeimage; if (vb->baddr && vb->bsize < vb->size) { ret = -EINVAL; @@ -999,6 +989,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_VYUY8_2X8, @@ -1008,6 +999,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YUYV8_2X8, @@ -1017,6 +1009,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YVYU8_2X8, @@ -1026,6 +1019,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, @@ -1035,6 +1029,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, @@ -1044,6 +1039,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB565_2X8_BE, @@ -1053,6 +1049,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB565_2X8_LE, @@ -1062,6 +1059,7 @@ static const struct soc_mbus_lookup omap1_cam_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, }; diff --git a/drivers/media/video/omap24xxcam-dma.c b/drivers/media/video/omap24xxcam-dma.c index 3ea38a8def8e..b5ae170de4a5 100644 --- a/drivers/media/video/omap24xxcam-dma.c +++ b/drivers/media/video/omap24xxcam-dma.c @@ -38,7 +38,7 @@ */ /* Ack all interrupt on CSR and IRQSTATUS_L0 */ -static void omap24xxcam_dmahw_ack_all(unsigned long base) +static void omap24xxcam_dmahw_ack_all(void __iomem *base) { u32 csr; int i; @@ -52,7 +52,7 @@ static void omap24xxcam_dmahw_ack_all(unsigned long base) } /* Ack dmach on CSR and IRQSTATUS_L0 */ -static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach) +static u32 omap24xxcam_dmahw_ack_ch(void __iomem *base, int dmach) { u32 csr; @@ -65,12 +65,12 @@ static u32 omap24xxcam_dmahw_ack_ch(unsigned long base, int dmach) return csr; } -static int omap24xxcam_dmahw_running(unsigned long base, int dmach) +static int omap24xxcam_dmahw_running(void __iomem *base, int dmach) { return omap24xxcam_reg_in(base, CAMDMA_CCR(dmach)) & CAMDMA_CCR_ENABLE; } -static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach, +static void omap24xxcam_dmahw_transfer_setup(void __iomem *base, int dmach, dma_addr_t start, u32 len) { omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), @@ -112,7 +112,7 @@ static void omap24xxcam_dmahw_transfer_setup(unsigned long base, int dmach, | CAMDMA_CICR_DROP_IE); } -static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach) +static void omap24xxcam_dmahw_transfer_start(void __iomem *base, int dmach) { omap24xxcam_reg_out(base, CAMDMA_CCR(dmach), CAMDMA_CCR_SEL_SRC_DST_SYNC @@ -124,7 +124,7 @@ static void omap24xxcam_dmahw_transfer_start(unsigned long base, int dmach) | CAMDMA_CCR_SYNCHRO_CAMERA); } -static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach, +static void omap24xxcam_dmahw_transfer_chain(void __iomem *base, int dmach, int free_dmach) { int prev_dmach, ch; @@ -160,7 +160,7 @@ static void omap24xxcam_dmahw_transfer_chain(unsigned long base, int dmach, * controller may not be idle after this routine completes, because * the completion routines might start new transfers. */ -static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach) +static void omap24xxcam_dmahw_abort_ch(void __iomem *base, int dmach) { /* mask all interrupts from this channel */ omap24xxcam_reg_out(base, CAMDMA_CICR(dmach), 0); @@ -171,7 +171,7 @@ static void omap24xxcam_dmahw_abort_ch(unsigned long base, int dmach) omap24xxcam_reg_merge(base, CAMDMA_CCR(dmach), 0, CAMDMA_CCR_ENABLE); } -static void omap24xxcam_dmahw_init(unsigned long base) +static void omap24xxcam_dmahw_init(void __iomem *base) { omap24xxcam_reg_out(base, CAMDMA_OCP_SYSCONFIG, CAMDMA_OCP_SYSCONFIG_MIDLEMODE_FSTANDBY @@ -362,7 +362,7 @@ void omap24xxcam_dma_hwinit(struct omap24xxcam_dma *dma) } static void omap24xxcam_dma_init(struct omap24xxcam_dma *dma, - unsigned long base) + void __iomem *base) { int ch; @@ -577,7 +577,7 @@ void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma) } void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, - unsigned long base, + void __iomem *base, void (*reset_callback)(unsigned long data), unsigned long reset_callback_data) { diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c index 7d3864144368..e5015b0d5508 100644 --- a/drivers/media/video/omap24xxcam.c +++ b/drivers/media/video/omap24xxcam.c @@ -1776,8 +1776,7 @@ static int __devinit omap24xxcam_probe(struct platform_device *pdev) cam->mmio_size = resource_size(mem); /* map the region */ - cam->mmio_base = (unsigned long) - ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); + cam->mmio_base = ioremap_nocache(cam->mmio_base_phys, cam->mmio_size); if (!cam->mmio_base) { dev_err(cam->dev, "cannot map camera register I/O region\n"); goto err; diff --git a/drivers/media/video/omap24xxcam.h b/drivers/media/video/omap24xxcam.h index 2ce67f5a48d5..d59727afe894 100644 --- a/drivers/media/video/omap24xxcam.h +++ b/drivers/media/video/omap24xxcam.h @@ -429,7 +429,7 @@ struct sgdma_state { struct omap24xxcam_dma { spinlock_t lock; /* Lock for the whole structure. */ - unsigned long base; /* base address for dma controller */ + void __iomem *base; /* base address for dma controller */ /* While dma_stop!=0, an attempt to start a new DMA transfer will * fail. @@ -491,7 +491,7 @@ struct omap24xxcam_device { /*** hardware resources ***/ unsigned int irq; - unsigned long mmio_base; + void __iomem *mmio_base; unsigned long mmio_base_phys; unsigned long mmio_size; @@ -544,22 +544,22 @@ struct omap24xxcam_fh { * */ -static inline u32 omap24xxcam_reg_in(unsigned long base, u32 offset) +static inline u32 omap24xxcam_reg_in(u32 __iomem *base, u32 offset) { return readl(base + offset); } -static inline u32 omap24xxcam_reg_out(unsigned long base, u32 offset, +static inline u32 omap24xxcam_reg_out(u32 __iomem *base, u32 offset, u32 val) { writel(val, base + offset); return val; } -static inline u32 omap24xxcam_reg_merge(unsigned long base, u32 offset, +static inline u32 omap24xxcam_reg_merge(u32 __iomem *base, u32 offset, u32 val, u32 mask) { - u32 addr = base + offset; + u32 __iomem *addr = base + offset; u32 new_val = (readl(addr) & ~mask) | (val & mask); writel(new_val, addr); @@ -585,7 +585,7 @@ int omap24xxcam_sgdma_queue(struct omap24xxcam_sgdma *sgdma, int len, sgdma_callback_t callback, void *arg); void omap24xxcam_sgdma_sync(struct omap24xxcam_sgdma *sgdma); void omap24xxcam_sgdma_init(struct omap24xxcam_sgdma *sgdma, - unsigned long base, + void __iomem *base, void (*reset_callback)(unsigned long data), unsigned long reset_callback_data); void omap24xxcam_sgdma_exit(struct omap24xxcam_sgdma *sgdma); diff --git a/drivers/media/video/omap3isp/isp.c b/drivers/media/video/omap3isp/isp.c index 12d5f923e1d0..1c347633e663 100644 --- a/drivers/media/video/omap3isp/isp.c +++ b/drivers/media/video/omap3isp/isp.c @@ -329,19 +329,6 @@ void omap3isp_configure_bridge(struct isp_device *isp, isp_reg_writel(isp, ispctrl_val, OMAP3_ISP_IOMEM_MAIN, ISP_CTRL); } -/** - * isp_set_pixel_clock - Configures the ISP pixel clock - * @isp: OMAP3 ISP device - * @pixelclk: Average pixel clock in Hz - * - * Set the average pixel clock required by the sensor. The ISP will use the - * lowest possible memory bandwidth settings compatible with the clock. - **/ -static void isp_set_pixel_clock(struct isp_device *isp, unsigned int pixelclk) -{ - isp->isp_ccdc.vpcfg.pixelclk = pixelclk; -} - void omap3isp_hist_dma_done(struct isp_device *isp) { if (omap3isp_ccdc_busy(&isp->isp_ccdc) || @@ -739,6 +726,17 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, unsigned long flags; int ret; + /* If the preview engine crashed it might not respond to read/write + * operations on the L4 bus. This would result in a bus fault and a + * kernel oops. Refuse to start streaming in that case. This check must + * be performed before the loop below to avoid starting entities if the + * pipeline won't start anyway (those entities would then likely fail to + * stop, making the problem worse). + */ + if ((pipe->entities & isp->crashed) & + (1U << isp->isp_prev.subdev.entity.id)) + return -EIO; + spin_lock_irqsave(&pipe->lock, flags); pipe->state &= ~(ISP_PIPELINE_IDLE_INPUT | ISP_PIPELINE_IDLE_OUTPUT); spin_unlock_irqrestore(&pipe->lock, flags); @@ -774,14 +772,6 @@ static int isp_pipeline_enable(struct isp_pipeline *pipe, } } - /* Frame number propagation. In continuous streaming mode the number - * is incremented in the frame start ISR. In mem-to-mem mode - * singleshot is used and frame start IRQs are not available. - * Thus we have to increment the number here. - */ - if (pipe->do_propagation && mode == ISP_PIPELINE_STREAM_SINGLESHOT) - atomic_inc(&pipe->frame_number); - return 0; } @@ -879,13 +869,15 @@ static int isp_pipeline_disable(struct isp_pipeline *pipe) if (ret) { dev_info(isp->dev, "Unable to stop %s\n", subdev->name); + /* If the entity failed to stopped, assume it has + * crashed. Mark it as such, the ISP will be reset when + * applications will release it. + */ + isp->crashed |= 1U << subdev->entity.id; failure = -ETIMEDOUT; } } - if (failure < 0) - isp->needs_reset = true; - return failure; } @@ -1069,6 +1061,7 @@ static int isp_reset(struct isp_device *isp) udelay(1); } + isp->crashed = 0; return 0; } @@ -1495,11 +1488,13 @@ void omap3isp_put(struct isp_device *isp) BUG_ON(isp->ref_count == 0); if (--isp->ref_count == 0) { isp_disable_interrupts(isp); - isp_save_ctx(isp); - if (isp->needs_reset) { + if (isp->domain) + isp_save_ctx(isp); + /* Reset the ISP if an entity has failed to stop. This is the + * only way to recover from such conditions. + */ + if (isp->crashed) isp_reset(isp); - isp->needs_reset = false; - } isp_disable_clocks(isp); } mutex_unlock(&isp->isp_mutex); @@ -1970,7 +1965,7 @@ error_csiphy: * * Always returns 0. */ -static int isp_remove(struct platform_device *pdev) +static int __devexit isp_remove(struct platform_device *pdev) { struct isp_device *isp = platform_get_drvdata(pdev); int i; @@ -1981,6 +1976,7 @@ static int isp_remove(struct platform_device *pdev) omap3isp_get(isp); iommu_detach_device(isp->domain, &pdev->dev); iommu_domain_free(isp->domain); + isp->domain = NULL; omap3isp_put(isp); free_irq(isp->irq_num, isp); @@ -2050,7 +2046,7 @@ static int isp_map_mem_resource(struct platform_device *pdev, * -EINVAL if couldn't install ISR, * or clk_get return error value. */ -static int isp_probe(struct platform_device *pdev) +static int __devinit isp_probe(struct platform_device *pdev) { struct isp_platform_data *pdata = pdev->dev.platform_data; struct isp_device *isp; @@ -2068,7 +2064,6 @@ static int isp_probe(struct platform_device *pdev) isp->autoidle = autoidle; isp->platform_cb.set_xclk = isp_set_xclk; - isp->platform_cb.set_pixel_clock = isp_set_pixel_clock; mutex_init(&isp->isp_mutex); spin_lock_init(&isp->stat_lock); @@ -2218,7 +2213,7 @@ MODULE_DEVICE_TABLE(platform, omap3isp_id_table); static struct platform_driver omap3isp_driver = { .probe = isp_probe, - .remove = isp_remove, + .remove = __devexit_p(isp_remove), .id_table = omap3isp_id_table, .driver = { .owner = THIS_MODULE, diff --git a/drivers/media/video/omap3isp/isp.h b/drivers/media/video/omap3isp/isp.h index d96603eb0d17..fc7af3e32efd 100644 --- a/drivers/media/video/omap3isp/isp.h +++ b/drivers/media/video/omap3isp/isp.h @@ -129,7 +129,6 @@ struct isp_platform_callback { int (*csiphy_config)(struct isp_csiphy *phy, struct isp_csiphy_dphy_cfg *dphy, struct isp_csiphy_lanes_cfg *lanes); - void (*set_pixel_clock)(struct isp_device *isp, unsigned int pixelclk); }; /* @@ -145,6 +144,7 @@ struct isp_platform_callback { * @raw_dmamask: Raw DMA mask * @stat_lock: Spinlock for handling statistics * @isp_mutex: Mutex for serializing requests to ISP. + * @crashed: Bitmask of crashed entities (indexed by entity ID) * @has_context: Context has been saved at least once and can be restored. * @ref_count: Reference count for handling multiple ISP requests. * @cam_ick: Pointer to camera interface clock structure. @@ -184,7 +184,7 @@ struct isp_device { /* ISP Obj */ spinlock_t stat_lock; /* common lock for statistic drivers */ struct mutex isp_mutex; /* For handling ref_count field */ - bool needs_reset; + u32 crashed; int has_context; int ref_count; unsigned int autoidle; @@ -237,10 +237,6 @@ void omap3isp_configure_bridge(struct isp_device *isp, const struct isp_parallel_platform_data *pdata, unsigned int shift); -#define ISP_XCLK_NONE 0 -#define ISP_XCLK_A 1 -#define ISP_XCLK_B 2 - struct isp_device *omap3isp_get(struct isp_device *isp); void omap3isp_put(struct isp_device *isp); diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index eaabc27f0fa2..7e32331b60fb 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c @@ -38,6 +38,9 @@ #include "ispreg.h" #include "ispccdc.h" +#define CCDC_MIN_WIDTH 32 +#define CCDC_MIN_HEIGHT 32 + static struct v4l2_mbus_framefmt * __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, unsigned int pad, enum v4l2_subdev_format_whence which); @@ -836,8 +839,8 @@ static void ccdc_config_vp(struct isp_ccdc_device *ccdc) if (pipe->input) div = DIV_ROUND_UP(l3_ick, pipe->max_rate); - else if (ccdc->vpcfg.pixelclk) - div = l3_ick / ccdc->vpcfg.pixelclk; + else if (pipe->external_rate) + div = l3_ick / pipe->external_rate; div = clamp(div, 2U, max_div); fmtcfg_vp |= (div - 2) << ISPCCDC_FMTCFG_VPIF_FRQ_SHIFT; @@ -1118,6 +1121,7 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) struct isp_parallel_platform_data *pdata = NULL; struct v4l2_subdev *sensor; struct v4l2_mbus_framefmt *format; + const struct v4l2_rect *crop; const struct isp_format_info *fmt_info; struct v4l2_subdev_format fmt_src; unsigned int depth_out; @@ -1211,14 +1215,14 @@ static void ccdc_configure(struct isp_ccdc_device *ccdc) OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VDINT); /* CCDC_PAD_SOURCE_OF */ - format = &ccdc->formats[CCDC_PAD_SOURCE_OF]; + crop = &ccdc->crop; - isp_reg_writel(isp, (0 << ISPCCDC_HORZ_INFO_SPH_SHIFT) | - ((format->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), + isp_reg_writel(isp, (crop->left << ISPCCDC_HORZ_INFO_SPH_SHIFT) | + ((crop->width - 1) << ISPCCDC_HORZ_INFO_NPH_SHIFT), OMAP3_ISP_IOMEM_CCDC, ISPCCDC_HORZ_INFO); - isp_reg_writel(isp, 0 << ISPCCDC_VERT_START_SLV0_SHIFT, + isp_reg_writel(isp, crop->top << ISPCCDC_VERT_START_SLV0_SHIFT, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_START); - isp_reg_writel(isp, (format->height - 1) + isp_reg_writel(isp, (crop->height - 1) << ISPCCDC_VERT_LINES_NLV_SHIFT, OMAP3_ISP_IOMEM_CCDC, ISPCCDC_VERT_LINES); @@ -1410,6 +1414,9 @@ static void ccdc_hs_vs_isr(struct isp_ccdc_device *ccdc) struct video_device *vdev = ccdc->subdev.devnode; struct v4l2_event event; + /* Frame number propagation */ + atomic_inc(&pipe->frame_number); + memset(&event, 0, sizeof(event)); event.type = V4L2_EVENT_FRAME_SYNC; event.u.frame_sync.frame_sequence = atomic_read(&pipe->frame_number); @@ -1703,7 +1710,7 @@ static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, if (sub->id != 0) return -EINVAL; - return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS); + return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL); } static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, @@ -1790,6 +1797,16 @@ __ccdc_get_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, return &ccdc->formats[pad]; } +static struct v4l2_rect * +__ccdc_get_crop(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, + enum v4l2_subdev_format_whence which) +{ + if (which == V4L2_SUBDEV_FORMAT_TRY) + return v4l2_subdev_get_try_crop(fh, CCDC_PAD_SOURCE_OF); + else + return &ccdc->crop; +} + /* * ccdc_try_format - Try video format on a pad * @ccdc: ISP CCDC device @@ -1806,6 +1823,7 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, const struct isp_format_info *info; unsigned int width = fmt->width; unsigned int height = fmt->height; + struct v4l2_rect *crop; unsigned int i; switch (pad) { @@ -1831,14 +1849,10 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, which); memcpy(fmt, format, sizeof(*fmt)); - /* The data formatter truncates the number of horizontal output - * pixels to a multiple of 16. To avoid clipping data, allow - * callers to request an output size bigger than the input size - * up to the nearest multiple of 16. - */ - fmt->width = clamp_t(u32, width, 32, fmt->width + 15); - fmt->width &= ~15; - fmt->height = clamp_t(u32, height, 32, fmt->height); + /* Hardcode the output size to the crop rectangle size. */ + crop = __ccdc_get_crop(ccdc, fh, which); + fmt->width = crop->width; + fmt->height = crop->height; break; case CCDC_PAD_SOURCE_VP: @@ -1865,6 +1879,49 @@ ccdc_try_format(struct isp_ccdc_device *ccdc, struct v4l2_subdev_fh *fh, fmt->field = V4L2_FIELD_NONE; } +/* + * ccdc_try_crop - Validate a crop rectangle + * @ccdc: ISP CCDC device + * @sink: format on the sink pad + * @crop: crop rectangle to be validated + */ +static void ccdc_try_crop(struct isp_ccdc_device *ccdc, + const struct v4l2_mbus_framefmt *sink, + struct v4l2_rect *crop) +{ + const struct isp_format_info *info; + unsigned int max_width; + + /* For Bayer formats, restrict left/top and width/height to even values + * to keep the Bayer pattern. + */ + info = omap3isp_video_format_info(sink->code); + if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { + crop->left &= ~1; + crop->top &= ~1; + } + + crop->left = clamp_t(u32, crop->left, 0, sink->width - CCDC_MIN_WIDTH); + crop->top = clamp_t(u32, crop->top, 0, sink->height - CCDC_MIN_HEIGHT); + + /* The data formatter truncates the number of horizontal output pixels + * to a multiple of 16. To avoid clipping data, allow callers to request + * an output size bigger than the input size up to the nearest multiple + * of 16. + */ + max_width = (sink->width - crop->left + 15) & ~15; + crop->width = clamp_t(u32, crop->width, CCDC_MIN_WIDTH, max_width) + & ~15; + crop->height = clamp_t(u32, crop->height, CCDC_MIN_HEIGHT, + sink->height - crop->top); + + /* Odd width/height values don't make sense for Bayer formats. */ + if (info->flavor != V4L2_MBUS_FMT_Y8_1X8) { + crop->width &= ~1; + crop->height &= ~1; + } +} + /* * ccdc_enum_mbus_code - Handle pixel format enumeration * @sd : pointer to v4l2 subdev structure @@ -1936,6 +1993,93 @@ static int ccdc_enum_frame_size(struct v4l2_subdev *sd, return 0; } +/* + * ccdc_get_selection - Retrieve a selection rectangle on a pad + * @sd: ISP CCDC V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the output formatter + * source pad. + * + * Return 0 on success or a negative error code otherwise. + */ +static int ccdc_get_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->pad != CCDC_PAD_SOURCE_OF) + return -EINVAL; + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + ccdc_try_crop(ccdc, format, &sel->r); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * ccdc_set_selection - Set a selection rectangle on a pad + * @sd: ISP CCDC V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the output + * formatter source pad. + * + * Return 0 on success or a negative error code otherwise. + */ +static int ccdc_set_selection(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != CCDC_PAD_SOURCE_OF) + return -EINVAL; + + /* The crop rectangle can't be changed while streaming. */ + if (ccdc->state != ISP_PIPELINE_STREAM_STOPPED) + return -EBUSY; + + /* Modifying the crop rectangle always changes the format on the source + * pad. If the KEEP_CONFIG flag is set, just return the current crop + * rectangle. + */ + if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + sel->r = *__ccdc_get_crop(ccdc, fh, sel->which); + return 0; + } + + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SINK, sel->which); + ccdc_try_crop(ccdc, format, &sel->r); + *__ccdc_get_crop(ccdc, fh, sel->which) = sel->r; + + /* Update the source format. */ + format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, sel->which); + ccdc_try_format(ccdc, fh, CCDC_PAD_SOURCE_OF, format, sel->which); + + return 0; +} + /* * ccdc_get_format - Retrieve the video format on a pad * @sd : ISP CCDC V4L2 subdevice @@ -1973,6 +2117,7 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, { struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; + struct v4l2_rect *crop; format = __ccdc_get_format(ccdc, fh, fmt->pad, fmt->which); if (format == NULL) @@ -1983,6 +2128,16 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, /* Propagate the format from sink to source */ if (fmt->pad == CCDC_PAD_SINK) { + /* Reset the crop rectangle. */ + crop = __ccdc_get_crop(ccdc, fh, fmt->which); + crop->left = 0; + crop->top = 0; + crop->width = fmt->format.width; + crop->height = fmt->format.height; + + ccdc_try_crop(ccdc, &fmt->format, crop); + + /* Update the source formats. */ format = __ccdc_get_format(ccdc, fh, CCDC_PAD_SOURCE_OF, fmt->which); *format = fmt->format; @@ -1999,6 +2154,69 @@ static int ccdc_set_format(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, return 0; } +/* + * Decide whether desired output pixel code can be obtained with + * the lane shifter by shifting the input pixel code. + * @in: input pixelcode to shifter + * @out: output pixelcode from shifter + * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0] + * + * return true if the combination is possible + * return false otherwise + */ +static bool ccdc_is_shiftable(enum v4l2_mbus_pixelcode in, + enum v4l2_mbus_pixelcode out, + unsigned int additional_shift) +{ + const struct isp_format_info *in_info, *out_info; + + if (in == out) + return true; + + in_info = omap3isp_video_format_info(in); + out_info = omap3isp_video_format_info(out); + + if ((in_info->flavor == 0) || (out_info->flavor == 0)) + return false; + + if (in_info->flavor != out_info->flavor) + return false; + + return in_info->bpp - out_info->bpp + additional_shift <= 6; +} + +static int ccdc_link_validate(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + struct isp_ccdc_device *ccdc = v4l2_get_subdevdata(sd); + unsigned long parallel_shift; + + /* Check if the two ends match */ + if (source_fmt->format.width != sink_fmt->format.width || + source_fmt->format.height != sink_fmt->format.height) + return -EPIPE; + + /* We've got a parallel sensor here. */ + if (ccdc->input == CCDC_INPUT_PARALLEL) { + struct isp_parallel_platform_data *pdata = + &((struct isp_v4l2_subdevs_group *) + media_entity_to_v4l2_subdev(link->source->entity) + ->host_priv)->bus.parallel; + parallel_shift = pdata->data_lane_shift * 2; + } else { + parallel_shift = 0; + } + + /* Lane shifter may be used to drop bits on CCDC sink pad */ + if (!ccdc_is_shiftable(source_fmt->format.code, + sink_fmt->format.code, parallel_shift)) + return -EPIPE; + + return 0; +} + /* * ccdc_init_formats - Initialize formats on all pads * @sd: ISP CCDC V4L2 subdevice @@ -2041,6 +2259,9 @@ static const struct v4l2_subdev_pad_ops ccdc_v4l2_pad_ops = { .enum_frame_size = ccdc_enum_frame_size, .get_fmt = ccdc_get_format, .set_fmt = ccdc_set_format, + .get_selection = ccdc_get_selection, + .set_selection = ccdc_set_selection, + .link_validate = ccdc_link_validate, }; /* V4L2 subdev operations */ @@ -2150,6 +2371,7 @@ static int ccdc_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations ccdc_media_ops = { .link_setup = ccdc_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_ccdc_unregister_entities(struct isp_ccdc_device *ccdc) @@ -2276,8 +2498,6 @@ int omap3isp_ccdc_init(struct isp_device *isp) ccdc->clamp.oblen = 0; ccdc->clamp.dcsubval = 0; - ccdc->vpcfg.pixelclk = 0; - ccdc->update = OMAP3ISP_CCDC_BLCLAMP; ccdc_apply_controls(ccdc); diff --git a/drivers/media/video/omap3isp/ispccdc.h b/drivers/media/video/omap3isp/ispccdc.h index 6d0264bab75b..890f6b3a68fd 100644 --- a/drivers/media/video/omap3isp/ispccdc.h +++ b/drivers/media/video/omap3isp/ispccdc.h @@ -80,14 +80,6 @@ struct ispccdc_syncif { u8 bt_r656_en; }; -/* - * struct ispccdc_vp - Structure for Video Port parameters - * @pixelclk: Input pixel clock in Hz - */ -struct ispccdc_vp { - unsigned int pixelclk; -}; - enum ispccdc_lsc_state { LSC_STATE_STOPPED = 0, LSC_STATE_STOPPING = 1, @@ -147,6 +139,7 @@ struct ispccdc_lsc { * @subdev: V4L2 subdevice * @pads: Sink and source media entity pads * @formats: Active video formats + * @crop: Active crop rectangle on the OF source pad * @input: Active input * @output: Active outputs * @video_out: Output video node @@ -161,7 +154,6 @@ struct ispccdc_lsc { * @update: Bitmask of controls to update during the next interrupt * @shadow_update: Controls update in progress by userspace * @syncif: Interface synchronization configuration - * @vpcfg: Video port configuration * @underrun: A buffer underrun occurred and a new buffer has been queued * @state: Streaming state * @lock: Serializes shadow_update with interrupt handler @@ -173,6 +165,7 @@ struct isp_ccdc_device { struct v4l2_subdev subdev; struct media_pad pads[CCDC_PADS_NUM]; struct v4l2_mbus_framefmt formats[CCDC_PADS_NUM]; + struct v4l2_rect crop; enum ccdc_input_entity input; unsigned int output; @@ -190,7 +183,6 @@ struct isp_ccdc_device { unsigned int shadow_update; struct ispccdc_syncif syncif; - struct ispccdc_vp vpcfg; unsigned int underrun:1; enum isp_pipeline_stream_state state; diff --git a/drivers/media/video/omap3isp/ispccp2.c b/drivers/media/video/omap3isp/ispccp2.c index 70ddbf35b223..85f0de85f37c 100644 --- a/drivers/media/video/omap3isp/ispccp2.c +++ b/drivers/media/video/omap3isp/ispccp2.c @@ -161,7 +161,6 @@ static void ccp2_pwr_cfg(struct isp_ccp2_device *ccp2) static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) { struct isp_device *isp = to_isp_device(ccp2); - struct isp_pipeline *pipe = to_isp_pipeline(&ccp2->subdev.entity); int i; if (enable && ccp2->vdds_csib) @@ -178,19 +177,6 @@ static void ccp2_if_enable(struct isp_ccp2_device *ccp2, u8 enable) ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN, enable ? (ISPCCP2_CTRL_MODE | ISPCCP2_CTRL_IF_EN) : 0); - /* For frame count propagation */ - if (pipe->do_propagation) { - /* We may want the Frame Start IRQ from LC0 */ - if (enable) - isp_reg_set(isp, OMAP3_ISP_IOMEM_CCP2, - ISPCCP2_LC01_IRQENABLE, - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); - else - isp_reg_clr(isp, OMAP3_ISP_IOMEM_CCP2, - ISPCCP2_LC01_IRQENABLE, - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ); - } - if (!enable && ccp2->vdds_csib) regulator_disable(ccp2->vdds_csib); } @@ -350,7 +336,6 @@ static void ccp2_lcx_config(struct isp_ccp2_device *ccp2, ISPCCP2_LC01_IRQSTATUS_LC0_CRC_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FSP_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FW_IRQ | - ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_FSC_IRQ | ISPCCP2_LC01_IRQSTATUS_LC0_SSC_IRQ; @@ -613,14 +598,6 @@ void omap3isp_ccp2_isr(struct isp_ccp2_device *ccp2) if (omap3isp_module_sync_is_stopping(&ccp2->wait, &ccp2->stopping)) return; - /* Frame number propagation */ - if (lcx_irqstatus & ISPCCP2_LC01_IRQSTATUS_LC0_FS_IRQ) { - struct isp_pipeline *pipe = - to_isp_pipeline(&ccp2->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - } - /* Handle queued buffers on frame end interrupts */ if (lcm_irqstatus & ISPCCP2_LCM_IRQSTATUS_EOF_IRQ) ccp2_isr_buffer(ccp2); @@ -1021,6 +998,7 @@ static int ccp2_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations ccp2_media_ops = { .link_setup = ccp2_link_setup, + .link_validate = v4l2_subdev_link_validate, }; /* diff --git a/drivers/media/video/omap3isp/ispcsi2.c b/drivers/media/video/omap3isp/ispcsi2.c index fcb5168996a7..a1724362b6d5 100644 --- a/drivers/media/video/omap3isp/ispcsi2.c +++ b/drivers/media/video/omap3isp/ispcsi2.c @@ -378,21 +378,17 @@ static void csi2_timing_config(struct isp_device *isp, static void csi2_irq_ctx_set(struct isp_device *isp, struct isp_csi2_device *csi2, int enable) { - u32 reg = ISPCSI2_CTX_IRQSTATUS_FE_IRQ; int i; - if (csi2->use_fs_irq) - reg |= ISPCSI2_CTX_IRQSTATUS_FS_IRQ; - for (i = 0; i < 8; i++) { - isp_reg_writel(isp, reg, csi2->regs1, + isp_reg_writel(isp, ISPCSI2_CTX_IRQSTATUS_FE_IRQ, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(i)); if (enable) isp_reg_set(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), - reg); + ISPCSI2_CTX_IRQSTATUS_FE_IRQ); else isp_reg_clr(isp, csi2->regs1, ISPCSI2_CTX_IRQENABLE(i), - reg); + ISPCSI2_CTX_IRQSTATUS_FE_IRQ); } } @@ -690,14 +686,6 @@ static void csi2_isr_ctx(struct isp_csi2_device *csi2, status = isp_reg_readl(isp, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); isp_reg_writel(isp, status, csi2->regs1, ISPCSI2_CTX_IRQSTATUS(n)); - /* Propagate frame number */ - if (status & ISPCSI2_CTX_IRQSTATUS_FS_IRQ) { - struct isp_pipeline *pipe = - to_isp_pipeline(&csi2->subdev.entity); - if (pipe->do_propagation) - atomic_inc(&pipe->frame_number); - } - if (!(status & ISPCSI2_CTX_IRQSTATUS_FE_IRQ)) return; @@ -1047,14 +1035,12 @@ static int csi2_set_stream(struct v4l2_subdev *sd, int enable) { struct isp_csi2_device *csi2 = v4l2_get_subdevdata(sd); struct isp_device *isp = csi2->isp; - struct isp_pipeline *pipe = to_isp_pipeline(&csi2->subdev.entity); struct isp_video *video_out = &csi2->video_out; switch (enable) { case ISP_PIPELINE_STREAM_CONTINUOUS: if (omap3isp_csiphy_acquire(csi2->phy) < 0) return -ENODEV; - csi2->use_fs_irq = pipe->do_propagation; if (csi2->output & CSI2_OUTPUT_MEMORY) omap3isp_sbl_enable(isp, OMAP3_ISP_SBL_CSI2A_WRITE); csi2_configure(csi2); @@ -1181,6 +1167,7 @@ static int csi2_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations csi2_media_ops = { .link_setup = csi2_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_csi2_unregister_entities(struct isp_csi2_device *csi2) diff --git a/drivers/media/video/omap3isp/ispcsi2.h b/drivers/media/video/omap3isp/ispcsi2.h index 885ad79a7678..c57729b7e86e 100644 --- a/drivers/media/video/omap3isp/ispcsi2.h +++ b/drivers/media/video/omap3isp/ispcsi2.h @@ -145,7 +145,6 @@ struct isp_csi2_device { u32 output; /* output to CCDC, memory or both? */ bool dpcm_decompress; unsigned int frame_skip; - bool use_fs_irq; struct isp_csiphy *phy; struct isp_csi2_ctx_cfg contexts[ISP_CSI2_MAX_CTX_NUM + 1]; diff --git a/drivers/media/video/omap3isp/ispcsiphy.c b/drivers/media/video/omap3isp/ispcsiphy.c index 5be37ce7d0c2..348f67ebbbc9 100644 --- a/drivers/media/video/omap3isp/ispcsiphy.c +++ b/drivers/media/video/omap3isp/ispcsiphy.c @@ -186,7 +186,9 @@ int omap3isp_csiphy_acquire(struct isp_csiphy *phy) if (rval < 0) goto done; - omap3isp_csi2_reset(phy->csi2); + rval = omap3isp_csi2_reset(phy->csi2); + if (rval < 0) + goto done; csiphy_dphy_config(phy); csiphy_lanes_config(phy); diff --git a/drivers/media/video/omap3isp/ispcsiphy.h b/drivers/media/video/omap3isp/ispcsiphy.h index 9596dc6830a6..e93a661e65d9 100644 --- a/drivers/media/video/omap3isp/ispcsiphy.h +++ b/drivers/media/video/omap3isp/ispcsiphy.h @@ -27,22 +27,11 @@ #ifndef OMAP3_ISP_CSI_PHY_H #define OMAP3_ISP_CSI_PHY_H +#include + struct isp_csi2_device; struct regulator; -struct csiphy_lane { - u8 pos; - u8 pol; -}; - -#define ISP_CSIPHY2_NUM_DATA_LANES 2 -#define ISP_CSIPHY1_NUM_DATA_LANES 1 - -struct isp_csiphy_lanes_cfg { - struct csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; - struct csiphy_lane clk; -}; - struct isp_csiphy_dphy_cfg { u8 ths_term; u8 ths_settle; diff --git a/drivers/media/video/omap3isp/isppreview.c b/drivers/media/video/omap3isp/isppreview.c index 6d0fb2c8c26d..8a4935ecc655 100644 --- a/drivers/media/video/omap3isp/isppreview.c +++ b/drivers/media/video/omap3isp/isppreview.c @@ -440,23 +440,6 @@ preview_enable_dcor(struct isp_prev_device *prev, u8 enable) ISPPRV_PCR_DCOREN); } -/* - * preview_enable_cfa - Enable/Disable the CFA Interpolation. - * @enable: 1 - Enables the CFA. - */ -static void -preview_enable_cfa(struct isp_prev_device *prev, u8 enable) -{ - struct isp_device *isp = to_isp_device(prev); - - if (enable) - isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_CFAEN); - else - isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, - ISPPRV_PCR_CFAEN); -} - /* * preview_enable_gammabypass - Enables/Disables the GammaByPass * @enable: 1 - Bypasses Gamma - 10bit input is cropped to 8MSB. @@ -608,12 +591,12 @@ preview_config_rgb_blending(struct isp_prev_device *prev, const void *rgb2rgb) } /* - * Configures the RGB-YCbYCr conversion matrix + * Configures the color space conversion (RGB toYCbYCr) matrix * @prev_csc: Structure containing the RGB to YCbYCr matrix and the * YCbCr offset. */ static void -preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) +preview_config_csc(struct isp_prev_device *prev, const void *prev_csc) { struct isp_device *isp = to_isp_device(prev); const struct omap3isp_prev_csc *csc = prev_csc; @@ -649,12 +632,18 @@ preview_config_rgb_to_ycbcr(struct isp_prev_device *prev, const void *prev_csc) static void preview_update_contrast(struct isp_prev_device *prev, u8 contrast) { - struct prev_params *params = &prev->params; + struct prev_params *params; + unsigned long flags; + + spin_lock_irqsave(&prev->params.lock, flags); + params = (prev->params.active & OMAP3ISP_PREV_CONTRAST) + ? &prev->params.params[0] : &prev->params.params[1]; if (params->contrast != (contrast * ISPPRV_CONTRAST_UNITS)) { params->contrast = contrast * ISPPRV_CONTRAST_UNITS; - prev->update |= PREV_CONTRAST; + params->update |= OMAP3ISP_PREV_CONTRAST; } + spin_unlock_irqrestore(&prev->params.lock, flags); } /* @@ -681,12 +670,18 @@ preview_config_contrast(struct isp_prev_device *prev, const void *params) static void preview_update_brightness(struct isp_prev_device *prev, u8 brightness) { - struct prev_params *params = &prev->params; + struct prev_params *params; + unsigned long flags; + + spin_lock_irqsave(&prev->params.lock, flags); + params = (prev->params.active & OMAP3ISP_PREV_BRIGHTNESS) + ? &prev->params.params[0] : &prev->params.params[1]; if (params->brightness != (brightness * ISPPRV_BRIGHT_UNITS)) { params->brightness = brightness * ISPPRV_BRIGHT_UNITS; - prev->update |= PREV_BRIGHTNESS; + params->update |= OMAP3ISP_PREV_BRIGHTNESS; } + spin_unlock_irqrestore(&prev->params.lock, flags); } /* @@ -721,158 +716,187 @@ preview_config_yc_range(struct isp_prev_device *prev, const void *yclimit) OMAP3_ISP_IOMEM_PREV, ISPPRV_SETUP_YC); } +static u32 +preview_params_lock(struct isp_prev_device *prev, u32 update, bool shadow) +{ + u32 active = prev->params.active; + + if (shadow) { + /* Mark all shadow parameters we are going to touch as busy. */ + prev->params.params[0].busy |= ~active & update; + prev->params.params[1].busy |= active & update; + } else { + /* Mark all active parameters we are going to touch as busy. */ + update = (prev->params.params[0].update & active) + | (prev->params.params[1].update & ~active); + + prev->params.params[0].busy |= active & update; + prev->params.params[1].busy |= ~active & update; + } + + return update; +} + +static void +preview_params_unlock(struct isp_prev_device *prev, u32 update, bool shadow) +{ + u32 active = prev->params.active; + + if (shadow) { + /* Set the update flag for shadow parameters that have been + * updated and clear the busy flag for all shadow parameters. + */ + prev->params.params[0].update |= (~active & update); + prev->params.params[1].update |= (active & update); + prev->params.params[0].busy &= active; + prev->params.params[1].busy &= ~active; + } else { + /* Clear the update flag for active parameters that have been + * applied and the busy flag for all active parameters. + */ + prev->params.params[0].update &= ~(active & update); + prev->params.params[1].update &= ~(~active & update); + prev->params.params[0].busy &= ~active; + prev->params.params[1].busy &= active; + } +} + +static void preview_params_switch(struct isp_prev_device *prev) +{ + u32 to_switch; + + /* Switch active parameters with updated shadow parameters when the + * shadow parameter has been updated and neither the active not the + * shadow parameter is busy. + */ + to_switch = (prev->params.params[0].update & ~prev->params.active) + | (prev->params.params[1].update & prev->params.active); + to_switch &= ~(prev->params.params[0].busy | + prev->params.params[1].busy); + if (to_switch == 0) + return; + + prev->params.active ^= to_switch; + + /* Remove the update flag for the shadow copy of parameters we have + * switched. + */ + prev->params.params[0].update &= ~(~prev->params.active & to_switch); + prev->params.params[1].update &= ~(prev->params.active & to_switch); +} + /* preview parameters update structure */ struct preview_update { - int cfg_bit; - int feature_bit; void (*config)(struct isp_prev_device *, const void *); void (*enable)(struct isp_prev_device *, u8); + unsigned int param_offset; + unsigned int param_size; + unsigned int config_offset; + bool skip; }; -static struct preview_update update_attrs[] = { - {OMAP3ISP_PREV_LUMAENH, PREV_LUMA_ENHANCE, +/* Keep the array indexed by the OMAP3ISP_PREV_* bit number. */ +static const struct preview_update update_attrs[] = { + /* OMAP3ISP_PREV_LUMAENH */ { preview_config_luma_enhancement, - preview_enable_luma_enhancement}, - {OMAP3ISP_PREV_INVALAW, PREV_INVERSE_ALAW, + preview_enable_luma_enhancement, + offsetof(struct prev_params, luma), + FIELD_SIZEOF(struct prev_params, luma), + offsetof(struct omap3isp_prev_update_config, luma), + }, /* OMAP3ISP_PREV_INVALAW */ { NULL, - preview_enable_invalaw}, - {OMAP3ISP_PREV_HRZ_MED, PREV_HORZ_MEDIAN_FILTER, + preview_enable_invalaw, + }, /* OMAP3ISP_PREV_HRZ_MED */ { preview_config_hmed, - preview_enable_hmed}, - {OMAP3ISP_PREV_CFA, PREV_CFA, + preview_enable_hmed, + offsetof(struct prev_params, hmed), + FIELD_SIZEOF(struct prev_params, hmed), + offsetof(struct omap3isp_prev_update_config, hmed), + }, /* OMAP3ISP_PREV_CFA */ { preview_config_cfa, - preview_enable_cfa}, - {OMAP3ISP_PREV_CHROMA_SUPP, PREV_CHROMA_SUPPRESS, + NULL, + offsetof(struct prev_params, cfa), + FIELD_SIZEOF(struct prev_params, cfa), + offsetof(struct omap3isp_prev_update_config, cfa), + }, /* OMAP3ISP_PREV_CHROMA_SUPP */ { preview_config_chroma_suppression, - preview_enable_chroma_suppression}, - {OMAP3ISP_PREV_WB, PREV_WB, + preview_enable_chroma_suppression, + offsetof(struct prev_params, csup), + FIELD_SIZEOF(struct prev_params, csup), + offsetof(struct omap3isp_prev_update_config, csup), + }, /* OMAP3ISP_PREV_WB */ { preview_config_whitebalance, - NULL}, - {OMAP3ISP_PREV_BLKADJ, PREV_BLKADJ, + NULL, + offsetof(struct prev_params, wbal), + FIELD_SIZEOF(struct prev_params, wbal), + offsetof(struct omap3isp_prev_update_config, wbal), + }, /* OMAP3ISP_PREV_BLKADJ */ { preview_config_blkadj, - NULL}, - {OMAP3ISP_PREV_RGB2RGB, PREV_RGB2RGB, + NULL, + offsetof(struct prev_params, blkadj), + FIELD_SIZEOF(struct prev_params, blkadj), + offsetof(struct omap3isp_prev_update_config, blkadj), + }, /* OMAP3ISP_PREV_RGB2RGB */ { preview_config_rgb_blending, - NULL}, - {OMAP3ISP_PREV_COLOR_CONV, PREV_COLOR_CONV, - preview_config_rgb_to_ycbcr, - NULL}, - {OMAP3ISP_PREV_YC_LIMIT, PREV_YCLIMITS, + NULL, + offsetof(struct prev_params, rgb2rgb), + FIELD_SIZEOF(struct prev_params, rgb2rgb), + offsetof(struct omap3isp_prev_update_config, rgb2rgb), + }, /* OMAP3ISP_PREV_COLOR_CONV */ { + preview_config_csc, + NULL, + offsetof(struct prev_params, csc), + FIELD_SIZEOF(struct prev_params, csc), + offsetof(struct omap3isp_prev_update_config, csc), + }, /* OMAP3ISP_PREV_YC_LIMIT */ { preview_config_yc_range, - NULL}, - {OMAP3ISP_PREV_DEFECT_COR, PREV_DEFECT_COR, + NULL, + offsetof(struct prev_params, yclimit), + FIELD_SIZEOF(struct prev_params, yclimit), + offsetof(struct omap3isp_prev_update_config, yclimit), + }, /* OMAP3ISP_PREV_DEFECT_COR */ { preview_config_dcor, - preview_enable_dcor}, - {OMAP3ISP_PREV_GAMMABYPASS, PREV_GAMMA_BYPASS, + preview_enable_dcor, + offsetof(struct prev_params, dcor), + FIELD_SIZEOF(struct prev_params, dcor), + offsetof(struct omap3isp_prev_update_config, dcor), + }, /* OMAP3ISP_PREV_GAMMABYPASS */ { NULL, - preview_enable_gammabypass}, - {OMAP3ISP_PREV_DRK_FRM_CAPTURE, PREV_DARK_FRAME_CAPTURE, + preview_enable_gammabypass, + }, /* OMAP3ISP_PREV_DRK_FRM_CAPTURE */ { NULL, - preview_enable_drkframe_capture}, - {OMAP3ISP_PREV_DRK_FRM_SUBTRACT, PREV_DARK_FRAME_SUBTRACT, + preview_enable_drkframe_capture, + }, /* OMAP3ISP_PREV_DRK_FRM_SUBTRACT */ { NULL, - preview_enable_drkframe}, - {OMAP3ISP_PREV_LENS_SHADING, PREV_LENS_SHADING, + preview_enable_drkframe, + }, /* OMAP3ISP_PREV_LENS_SHADING */ { preview_config_drkf_shadcomp, - preview_enable_drkframe}, - {OMAP3ISP_PREV_NF, PREV_NOISE_FILTER, + preview_enable_drkframe, + }, /* OMAP3ISP_PREV_NF */ { preview_config_noisefilter, - preview_enable_noisefilter}, - {OMAP3ISP_PREV_GAMMA, PREV_GAMMA, + preview_enable_noisefilter, + offsetof(struct prev_params, nf), + FIELD_SIZEOF(struct prev_params, nf), + offsetof(struct omap3isp_prev_update_config, nf), + }, /* OMAP3ISP_PREV_GAMMA */ { preview_config_gammacorrn, - NULL}, - {-1, PREV_CONTRAST, + NULL, + offsetof(struct prev_params, gamma), + FIELD_SIZEOF(struct prev_params, gamma), + offsetof(struct omap3isp_prev_update_config, gamma), + }, /* OMAP3ISP_PREV_CONTRAST */ { preview_config_contrast, - NULL}, - {-1, PREV_BRIGHTNESS, + NULL, + offsetof(struct prev_params, contrast), + 0, true, + }, /* OMAP3ISP_PREV_BRIGHTNESS */ { preview_config_brightness, - NULL}, + NULL, + offsetof(struct prev_params, brightness), + 0, true, + }, }; -/* - * __preview_get_ptrs - helper function which return pointers to members - * of params and config structures. - * @params - pointer to preview_params structure. - * @param - return pointer to appropriate structure field. - * @configs - pointer to update config structure. - * @config - return pointer to appropriate structure field. - * @bit - for which feature to return pointers. - * Return size of corresponding prev_params member - */ -static u32 -__preview_get_ptrs(struct prev_params *params, void **param, - struct omap3isp_prev_update_config *configs, - void __user **config, u32 bit) -{ -#define CHKARG(cfgs, cfg, field) \ - if (cfgs && cfg) { \ - *(cfg) = (cfgs)->field; \ - } - - switch (bit) { - case PREV_HORZ_MEDIAN_FILTER: - *param = ¶ms->hmed; - CHKARG(configs, config, hmed) - return sizeof(params->hmed); - case PREV_NOISE_FILTER: - *param = ¶ms->nf; - CHKARG(configs, config, nf) - return sizeof(params->nf); - break; - case PREV_CFA: - *param = ¶ms->cfa; - CHKARG(configs, config, cfa) - return sizeof(params->cfa); - case PREV_LUMA_ENHANCE: - *param = ¶ms->luma; - CHKARG(configs, config, luma) - return sizeof(params->luma); - case PREV_CHROMA_SUPPRESS: - *param = ¶ms->csup; - CHKARG(configs, config, csup) - return sizeof(params->csup); - case PREV_DEFECT_COR: - *param = ¶ms->dcor; - CHKARG(configs, config, dcor) - return sizeof(params->dcor); - case PREV_BLKADJ: - *param = ¶ms->blk_adj; - CHKARG(configs, config, blkadj) - return sizeof(params->blk_adj); - case PREV_YCLIMITS: - *param = ¶ms->yclimit; - CHKARG(configs, config, yclimit) - return sizeof(params->yclimit); - case PREV_RGB2RGB: - *param = ¶ms->rgb2rgb; - CHKARG(configs, config, rgb2rgb) - return sizeof(params->rgb2rgb); - case PREV_COLOR_CONV: - *param = ¶ms->rgb2ycbcr; - CHKARG(configs, config, csc) - return sizeof(params->rgb2ycbcr); - case PREV_WB: - *param = ¶ms->wbal; - CHKARG(configs, config, wbal) - return sizeof(params->wbal); - case PREV_GAMMA: - *param = ¶ms->gamma; - CHKARG(configs, config, gamma) - return sizeof(params->gamma); - case PREV_CONTRAST: - *param = ¶ms->contrast; - return 0; - case PREV_BRIGHTNESS: - *param = ¶ms->brightness; - return 0; - default: - *param = NULL; - *config = NULL; - break; - } - return 0; -} - /* * preview_config - Copy and update local structure with userspace preview * configuration. @@ -885,84 +909,103 @@ __preview_get_ptrs(struct prev_params *params, void **param, static int preview_config(struct isp_prev_device *prev, struct omap3isp_prev_update_config *cfg) { - struct prev_params *params; - struct preview_update *attr; - int i, bit, rval = 0; + unsigned long flags; + unsigned int i; + int rval = 0; + u32 update; + u32 active; - params = &prev->params; + if (cfg->update == 0) + return 0; - if (prev->state != ISP_PIPELINE_STREAM_STOPPED) { - unsigned long flags; + /* Mark the shadow parameters we're going to update as busy. */ + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_lock(prev, cfg->update, true); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); - spin_lock_irqsave(&prev->lock, flags); - prev->shadow_update = 1; - spin_unlock_irqrestore(&prev->lock, flags); - } + update = 0; for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { - attr = &update_attrs[i]; - bit = 0; + const struct preview_update *attr = &update_attrs[i]; + struct prev_params *params; + unsigned int bit = 1 << i; - if (!(cfg->update & attr->cfg_bit)) + if (attr->skip || !(cfg->update & bit)) continue; - bit = cfg->flag & attr->cfg_bit; - if (bit) { - void *to = NULL, __user *from = NULL; - unsigned long sz = 0; + params = &prev->params.params[!!(active & bit)]; - sz = __preview_get_ptrs(params, &to, cfg, &from, - bit); - if (to && from && sz) { - if (copy_from_user(to, from, sz)) { + if (cfg->flag & bit) { + void __user *from = *(void * __user *) + ((void *)cfg + attr->config_offset); + void *to = (void *)params + attr->param_offset; + size_t size = attr->param_size; + + if (to && from && size) { + if (copy_from_user(to, from, size)) { rval = -EFAULT; break; } } - params->features |= attr->feature_bit; + params->features |= bit; } else { - params->features &= ~attr->feature_bit; + params->features &= ~bit; } - prev->update |= attr->feature_bit; + update |= bit; } - prev->shadow_update = 0; + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, true); + preview_params_switch(prev); + spin_unlock_irqrestore(&prev->params.lock, flags); + return rval; } /* * preview_setup_hw - Setup preview registers and/or internal memory * @prev: pointer to preview private structure + * @update: Bitmask of parameters to setup + * @active: Bitmask of parameters active in set 0 * Note: can be called from interrupt context * Return none */ -static void preview_setup_hw(struct isp_prev_device *prev) +static void preview_setup_hw(struct isp_prev_device *prev, u32 update, + u32 active) { - struct prev_params *params = &prev->params; - struct preview_update *attr; - int i, bit; - void *param_ptr; + unsigned int i; + u32 features; + + if (update == 0) + return; + + features = (prev->params.params[0].features & active) + | (prev->params.params[1].features & ~active); for (i = 0; i < ARRAY_SIZE(update_attrs); i++) { - attr = &update_attrs[i]; + const struct preview_update *attr = &update_attrs[i]; + struct prev_params *params; + unsigned int bit = 1 << i; + void *param_ptr; - if (!(prev->update & attr->feature_bit)) + if (!(update & bit)) continue; - bit = params->features & attr->feature_bit; - if (bit) { + + params = &prev->params.params[!(active & bit)]; + + if (params->features & bit) { if (attr->config) { - __preview_get_ptrs(params, ¶m_ptr, NULL, - NULL, bit); + param_ptr = (void *)params + attr->param_offset; attr->config(prev, param_ptr); } if (attr->enable) attr->enable(prev, 1); - } else + } else { if (attr->enable) attr->enable(prev, 0); - - prev->update &= ~attr->feature_bit; + } } } @@ -1000,19 +1043,44 @@ preview_config_ycpos(struct isp_prev_device *prev, static void preview_config_averager(struct isp_prev_device *prev, u8 average) { struct isp_device *isp = to_isp_device(prev); + struct prev_params *params; int reg = 0; - if (prev->params.cfa.format == OMAP3ISP_CFAFMT_BAYER) + params = (prev->params.active & OMAP3ISP_PREV_CFA) + ? &prev->params.params[0] : &prev->params.params[1]; + + if (params->cfa.format == OMAP3ISP_CFAFMT_BAYER) reg = ISPPRV_AVE_EVENDIST_2 << ISPPRV_AVE_EVENDIST_SHIFT | ISPPRV_AVE_ODDDIST_2 << ISPPRV_AVE_ODDDIST_SHIFT | average; - else if (prev->params.cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) + else if (params->cfa.format == OMAP3ISP_CFAFMT_RGBFOVEON) reg = ISPPRV_AVE_EVENDIST_3 << ISPPRV_AVE_EVENDIST_SHIFT | ISPPRV_AVE_ODDDIST_3 << ISPPRV_AVE_ODDDIST_SHIFT | average; isp_reg_writel(isp, reg, OMAP3_ISP_IOMEM_PREV, ISPPRV_AVE); } +/* + * preview_config_input_format - Configure the input format + * @prev: The preview engine + * @format: Format on the preview engine sink pad + * + * Enable CFA interpolation for Bayer formats and disable it for greyscale + * formats. + */ +static void preview_config_input_format(struct isp_prev_device *prev, + const struct v4l2_mbus_framefmt *format) +{ + struct isp_device *isp = to_isp_device(prev); + + if (format->code != V4L2_MBUS_FMT_Y10_1X10) + isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); + else + isp_reg_clr(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, + ISPPRV_PCR_CFAEN); +} + /* * preview_config_input_size - Configure the input frame size * @@ -1024,32 +1092,37 @@ static void preview_config_averager(struct isp_prev_device *prev, u8 average) * * See the explanation at the PREV_MARGIN_* definitions for more details. */ -static void preview_config_input_size(struct isp_prev_device *prev) +static void preview_config_input_size(struct isp_prev_device *prev, u32 active) { + const struct v4l2_mbus_framefmt *format = &prev->formats[PREV_PAD_SINK]; struct isp_device *isp = to_isp_device(prev); - struct prev_params *params = &prev->params; unsigned int sph = prev->crop.left; unsigned int eph = prev->crop.left + prev->crop.width - 1; unsigned int slv = prev->crop.top; unsigned int elv = prev->crop.top + prev->crop.height - 1; + u32 features; - if (params->features & PREV_CFA) { + if (format->code == V4L2_MBUS_FMT_Y10_1X10) { sph -= 2; eph += 2; slv -= 2; elv += 2; } - if (params->features & (PREV_DEFECT_COR | PREV_NOISE_FILTER)) { + + features = (prev->params.params[0].features & active) + | (prev->params.params[1].features & ~active); + + if (features & (OMAP3ISP_PREV_DEFECT_COR | OMAP3ISP_PREV_NF)) { sph -= 2; eph += 2; slv -= 2; elv += 2; } - if (params->features & PREV_HORZ_MEDIAN_FILTER) { + if (features & OMAP3ISP_PREV_HRZ_MED) { sph -= 2; eph += 2; } - if (params->features & (PREV_CHROMA_SUPPRESS | PREV_LUMA_ENHANCE)) + if (features & (OMAP3ISP_PREV_CHROMA_SUPP | OMAP3ISP_PREV_LUMAENH)) sph -= 2; isp_reg_writel(isp, (sph << ISPPRV_HORZ_INFO_SPH_SHIFT) | eph, @@ -1184,8 +1257,16 @@ int omap3isp_preview_busy(struct isp_prev_device *prev) */ void omap3isp_preview_restore_context(struct isp_device *isp) { - isp->isp_prev.update = PREV_FEATURES_END - 1; - preview_setup_hw(&isp->isp_prev); + struct isp_prev_device *prev = &isp->isp_prev; + const u32 update = OMAP3ISP_PREV_FEATURES_END - 1; + + prev->params.params[0].update = prev->params.active & update; + prev->params.params[1].update = ~prev->params.active & update; + + preview_setup_hw(prev, update, prev->params.active); + + prev->params.params[0].update = 0; + prev->params.params[1].update = 0; } /* @@ -1244,12 +1325,21 @@ static void preview_print_status(struct isp_prev_device *prev) /* * preview_init_params - init image processing parameters. * @prev: pointer to previewer private structure - * return none */ static void preview_init_params(struct isp_prev_device *prev) { - struct prev_params *params = &prev->params; - int i = 0; + struct prev_params *params; + unsigned int i; + + spin_lock_init(&prev->params.lock); + + prev->params.active = ~0; + prev->params.params[0].busy = 0; + prev->params.params[0].update = OMAP3ISP_PREV_FEATURES_END - 1; + prev->params.params[1].busy = 0; + prev->params.params[1].update = 0; + + params = &prev->params.params[0]; /* Init values */ params->contrast = ISPPRV_CONTRAST_DEF * ISPPRV_CONTRAST_UNITS; @@ -1277,22 +1367,22 @@ static void preview_init_params(struct isp_prev_device *prev) params->wbal.coef1 = FLR_WBAL_COEF; params->wbal.coef2 = FLR_WBAL_COEF; params->wbal.coef3 = FLR_WBAL_COEF; - params->blk_adj.red = FLR_BLKADJ_RED; - params->blk_adj.green = FLR_BLKADJ_GREEN; - params->blk_adj.blue = FLR_BLKADJ_BLUE; + params->blkadj.red = FLR_BLKADJ_RED; + params->blkadj.green = FLR_BLKADJ_GREEN; + params->blkadj.blue = FLR_BLKADJ_BLUE; params->rgb2rgb = flr_rgb2rgb; - params->rgb2ycbcr = flr_prev_csc; + params->csc = flr_prev_csc; params->yclimit.minC = ISPPRV_YC_MIN; params->yclimit.maxC = ISPPRV_YC_MAX; params->yclimit.minY = ISPPRV_YC_MIN; params->yclimit.maxY = ISPPRV_YC_MAX; - params->features = PREV_CFA | PREV_DEFECT_COR | PREV_NOISE_FILTER - | PREV_GAMMA | PREV_BLKADJ | PREV_YCLIMITS - | PREV_RGB2RGB | PREV_COLOR_CONV | PREV_WB - | PREV_BRIGHTNESS | PREV_CONTRAST; - - prev->update = PREV_FEATURES_END - 1; + params->features = OMAP3ISP_PREV_CFA | OMAP3ISP_PREV_DEFECT_COR + | OMAP3ISP_PREV_NF | OMAP3ISP_PREV_GAMMA + | OMAP3ISP_PREV_BLKADJ | OMAP3ISP_PREV_YC_LIMIT + | OMAP3ISP_PREV_RGB2RGB | OMAP3ISP_PREV_COLOR_CONV + | OMAP3ISP_PREV_WB | OMAP3ISP_PREV_BRIGHTNESS + | OMAP3ISP_PREV_CONTRAST; } /* @@ -1321,8 +1411,17 @@ static void preview_configure(struct isp_prev_device *prev) { struct isp_device *isp = to_isp_device(prev); struct v4l2_mbus_framefmt *format; + unsigned long flags; + u32 update; + u32 active; - preview_setup_hw(prev); + spin_lock_irqsave(&prev->params.lock, flags); + /* Mark all active parameters we are going to touch as busy. */ + update = preview_params_lock(prev, 0, false); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); + + preview_setup_hw(prev, update, active); if (prev->output & PREVIEW_OUTPUT_MEMORY) isp_reg_set(isp, OMAP3_ISP_IOMEM_PREV, ISPPRV_PCR, @@ -1343,7 +1442,8 @@ static void preview_configure(struct isp_prev_device *prev) preview_adjust_bandwidth(prev); - preview_config_input_size(prev); + preview_config_input_format(prev, format); + preview_config_input_size(prev, active); if (prev->input == PREVIEW_INPUT_CCDC) preview_config_inlineoffset(prev, 0); @@ -1360,6 +1460,10 @@ static void preview_configure(struct isp_prev_device *prev) preview_config_averager(prev, 0); preview_config_ycpos(prev, format->code); + + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, false); + spin_unlock_irqrestore(&prev->params.lock, flags); } /* ----------------------------------------------------------------------------- @@ -1448,25 +1552,30 @@ static void preview_isr_buffer(struct isp_prev_device *prev) void omap3isp_preview_isr(struct isp_prev_device *prev) { unsigned long flags; + u32 update; + u32 active; if (omap3isp_module_sync_is_stopping(&prev->wait, &prev->stopping)) return; - spin_lock_irqsave(&prev->lock, flags); - if (prev->shadow_update) - goto done; + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_switch(prev); + update = preview_params_lock(prev, 0, false); + active = prev->params.active; + spin_unlock_irqrestore(&prev->params.lock, flags); - preview_setup_hw(prev); - preview_config_input_size(prev); - -done: - spin_unlock_irqrestore(&prev->lock, flags); + preview_setup_hw(prev, update, active); + preview_config_input_size(prev, active); if (prev->input == PREVIEW_INPUT_MEMORY || prev->output & PREVIEW_OUTPUT_MEMORY) preview_isr_buffer(prev); else if (prev->state == ISP_PIPELINE_STREAM_CONTINUOUS) preview_enable_oneshot(prev); + + spin_lock_irqsave(&prev->params.lock, flags); + preview_params_unlock(prev, update, false); + spin_unlock_irqrestore(&prev->params.lock, flags); } /* ----------------------------------------------------------------------------- @@ -1552,7 +1661,6 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable) struct isp_video *video_out = &prev->video_out; struct isp_device *isp = to_isp_device(prev); struct device *dev = to_device(prev); - unsigned long flags; if (prev->state == ISP_PIPELINE_STREAM_STOPPED) { if (enable == ISP_PIPELINE_STREAM_STOPPED) @@ -1589,11 +1697,9 @@ static int preview_set_stream(struct v4l2_subdev *sd, int enable) if (omap3isp_module_sync_idle(&sd->entity, &prev->wait, &prev->stopping)) dev_dbg(dev, "%s: stop timeout.\n", sd->name); - spin_lock_irqsave(&prev->lock, flags); omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_READ); omap3isp_sbl_disable(isp, OMAP3_ISP_SBL_PREVIEW_WRITE); omap3isp_subclk_disable(isp, OMAP3_ISP_SUBCLK_PREVIEW); - spin_unlock_irqrestore(&prev->lock, flags); isp_video_dmaqueue_flags_clr(video_out); break; } @@ -1624,6 +1730,7 @@ __preview_get_crop(struct isp_prev_device *prev, struct v4l2_subdev_fh *fh, /* previewer format descriptions */ static const unsigned int preview_input_fmts[] = { + V4L2_MBUS_FMT_Y10_1X10, V4L2_MBUS_FMT_SGRBG10_1X10, V4L2_MBUS_FMT_SRGGB10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, @@ -1822,55 +1929,89 @@ static int preview_enum_frame_size(struct v4l2_subdev *sd, } /* - * preview_get_crop - Retrieve the crop rectangle on a pad + * preview_get_selection - Retrieve a selection rectangle on a pad * @sd: ISP preview V4L2 subdevice * @fh: V4L2 subdev file handle - * @crop: crop rectangle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the sink pad. * * Return 0 on success or a negative error code otherwise. */ -static int preview_get_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct isp_prev_device *prev = v4l2_get_subdevdata(sd); - - /* Cropping is only supported on the sink pad. */ - if (crop->pad != PREV_PAD_SINK) - return -EINVAL; - - crop->rect = *__preview_get_crop(prev, fh, crop->which); - return 0; -} - -/* - * preview_set_crop - Retrieve the crop rectangle on a pad - * @sd: ISP preview V4L2 subdevice - * @fh: V4L2 subdev file handle - * @crop: crop rectangle - * - * Return 0 on success or a negative error code otherwise. - */ -static int preview_set_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int preview_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct isp_prev_device *prev = v4l2_get_subdevdata(sd); struct v4l2_mbus_framefmt *format; - /* Cropping is only supported on the sink pad. */ - if (crop->pad != PREV_PAD_SINK) + if (sel->pad != PREV_PAD_SINK) + return -EINVAL; + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + format = __preview_get_format(prev, fh, PREV_PAD_SINK, + sel->which); + preview_try_crop(prev, format, &sel->r); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__preview_get_crop(prev, fh, sel->which); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * preview_set_selection - Set a selection rectangle on a pad + * @sd: ISP preview V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the sink pad. + * + * Return 0 on success or a negative error code otherwise. + */ +static int preview_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_prev_device *prev = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format; + + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != PREV_PAD_SINK) return -EINVAL; /* The crop rectangle can't be changed while streaming. */ if (prev->state != ISP_PIPELINE_STREAM_STOPPED) return -EBUSY; - format = __preview_get_format(prev, fh, PREV_PAD_SINK, crop->which); - preview_try_crop(prev, format, &crop->rect); - *__preview_get_crop(prev, fh, crop->which) = crop->rect; + /* Modifying the crop rectangle always changes the format on the source + * pad. If the KEEP_CONFIG flag is set, just return the current crop + * rectangle. + */ + if (sel->flags & V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG) { + sel->r = *__preview_get_crop(prev, fh, sel->which); + return 0; + } + + format = __preview_get_format(prev, fh, PREV_PAD_SINK, sel->which); + preview_try_crop(prev, format, &sel->r); + *__preview_get_crop(prev, fh, sel->which) = sel->r; /* Update the source format. */ - format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, crop->which); - preview_try_format(prev, fh, PREV_PAD_SOURCE, format, crop->which); + format = __preview_get_format(prev, fh, PREV_PAD_SOURCE, sel->which); + preview_try_format(prev, fh, PREV_PAD_SOURCE, format, sel->which); return 0; } @@ -1979,8 +2120,8 @@ static const struct v4l2_subdev_pad_ops preview_v4l2_pad_ops = { .enum_frame_size = preview_enum_frame_size, .get_fmt = preview_get_format, .set_fmt = preview_set_format, - .get_crop = preview_get_crop, - .set_crop = preview_set_crop, + .get_selection = preview_get_selection, + .set_selection = preview_set_selection, }; /* subdev operations */ @@ -2076,6 +2217,7 @@ static int preview_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations preview_media_ops = { .link_setup = preview_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_preview_unregister_entities(struct isp_prev_device *prev) @@ -2201,7 +2343,7 @@ error_video_in: } /* - * isp_preview_init - Previewer initialization. + * omap3isp_preview_init - Previewer initialization. * @dev : Pointer to ISP device * return -ENOMEM or zero on success */ @@ -2209,8 +2351,8 @@ int omap3isp_preview_init(struct isp_device *isp) { struct isp_prev_device *prev = &isp->isp_prev; - spin_lock_init(&prev->lock); init_waitqueue_head(&prev->wait); + preview_init_params(prev); return preview_init_entities(prev); diff --git a/drivers/media/video/omap3isp/isppreview.h b/drivers/media/video/omap3isp/isppreview.h index 09686607973c..6663ab64e4b1 100644 --- a/drivers/media/video/omap3isp/isppreview.h +++ b/drivers/media/video/omap3isp/isppreview.h @@ -45,29 +45,10 @@ #define ISPPRV_CONTRAST_HIGH 0xFF #define ISPPRV_CONTRAST_UNITS 0x1 -/* Features list */ -#define PREV_LUMA_ENHANCE OMAP3ISP_PREV_LUMAENH -#define PREV_INVERSE_ALAW OMAP3ISP_PREV_INVALAW -#define PREV_HORZ_MEDIAN_FILTER OMAP3ISP_PREV_HRZ_MED -#define PREV_CFA OMAP3ISP_PREV_CFA -#define PREV_CHROMA_SUPPRESS OMAP3ISP_PREV_CHROMA_SUPP -#define PREV_WB OMAP3ISP_PREV_WB -#define PREV_BLKADJ OMAP3ISP_PREV_BLKADJ -#define PREV_RGB2RGB OMAP3ISP_PREV_RGB2RGB -#define PREV_COLOR_CONV OMAP3ISP_PREV_COLOR_CONV -#define PREV_YCLIMITS OMAP3ISP_PREV_YC_LIMIT -#define PREV_DEFECT_COR OMAP3ISP_PREV_DEFECT_COR -#define PREV_GAMMA_BYPASS OMAP3ISP_PREV_GAMMABYPASS -#define PREV_DARK_FRAME_CAPTURE OMAP3ISP_PREV_DRK_FRM_CAPTURE -#define PREV_DARK_FRAME_SUBTRACT OMAP3ISP_PREV_DRK_FRM_SUBTRACT -#define PREV_LENS_SHADING OMAP3ISP_PREV_LENS_SHADING -#define PREV_NOISE_FILTER OMAP3ISP_PREV_NF -#define PREV_GAMMA OMAP3ISP_PREV_GAMMA - -#define PREV_CONTRAST (1 << 17) -#define PREV_BRIGHTNESS (1 << 18) -#define PREV_AVERAGER (1 << 19) -#define PREV_FEATURES_END (1 << 20) +/* Additional features not listed in linux/omap3isp.h */ +#define OMAP3ISP_PREV_CONTRAST (1 << 17) +#define OMAP3ISP_PREV_BRIGHTNESS (1 << 18) +#define OMAP3ISP_PREV_FEATURES_END (1 << 19) enum preview_input_entity { PREVIEW_INPUT_NONE, @@ -88,6 +69,8 @@ enum preview_ycpos_mode { /* * struct prev_params - Structure for all configuration + * @busy: Bitmask of busy parameters (being updated or used) + * @update: Bitmask of the parameters to be updated * @features: Set of features enabled. * @cfa: CFA coefficients. * @csup: Chroma suppression coefficients. @@ -96,15 +79,17 @@ enum preview_ycpos_mode { * @dcor: Noise filter coefficients. * @gamma: Gamma coefficients. * @wbal: White Balance parameters. - * @blk_adj: Black adjustment parameters. + * @blkadj: Black adjustment parameters. * @rgb2rgb: RGB blending parameters. - * @rgb2ycbcr: RGB to ycbcr parameters. + * @csc: Color space conversion (RGB to YCbCr) parameters. * @hmed: Horizontal median filter. * @yclimit: YC limits parameters. * @contrast: Contrast. * @brightness: Brightness. */ struct prev_params { + u32 busy; + u32 update; u32 features; struct omap3isp_prev_cfa cfa; struct omap3isp_prev_csup csup; @@ -113,35 +98,15 @@ struct prev_params { struct omap3isp_prev_dcor dcor; struct omap3isp_prev_gtables gamma; struct omap3isp_prev_wbal wbal; - struct omap3isp_prev_blkadj blk_adj; + struct omap3isp_prev_blkadj blkadj; struct omap3isp_prev_rgbtorgb rgb2rgb; - struct omap3isp_prev_csc rgb2ycbcr; + struct omap3isp_prev_csc csc; struct omap3isp_prev_hmed hmed; struct omap3isp_prev_yclimit yclimit; u8 contrast; u8 brightness; }; -/* - * struct isptables_update - Structure for Table Configuration. - * @update: Specifies which tables should be updated. - * @flag: Specifies which tables should be enabled. - * @nf: Pointer to structure for Noise Filter - * @lsc: Pointer to LSC gain table. (currently not used) - * @gamma: Pointer to gamma correction tables. - * @cfa: Pointer to color filter array configuration. - * @wbal: Pointer to colour and digital gain configuration. - */ -struct isptables_update { - u32 update; - u32 flag; - struct omap3isp_prev_nf *nf; - u32 *lsc; - struct omap3isp_prev_gtables *gamma; - struct omap3isp_prev_cfa *cfa; - struct omap3isp_prev_wbal *wbal; -}; - /* Sink and source previewer pads */ #define PREV_PAD_SINK 0 #define PREV_PAD_SOURCE 1 @@ -157,12 +122,11 @@ struct isptables_update { * @output: Bitmask of the active output * @video_in: Input video entity * @video_out: Output video entity - * @params: Module configuration data - * @shadow_update: If set, update the hardware configured in the next interrupt + * @params.params : Active and shadow parameters sets + * @params.active: Bitmask of parameters active in set 0 + * @params.lock: Parameters lock, protects params.active and params.shadow * @underrun: Whether the preview entity has queued buffers on the output * @state: Current preview pipeline state - * @lock: Shadow update lock - * @update: Bitmask of the parameters to be updated * * This structure is used to store the OMAP ISP Preview module Information. */ @@ -179,13 +143,15 @@ struct isp_prev_device { struct isp_video video_in; struct isp_video video_out; - struct prev_params params; - unsigned int shadow_update:1; + struct { + struct prev_params params[2]; + u32 active; + spinlock_t lock; + } params; + enum isp_pipeline_stream_state state; wait_queue_head_t wait; atomic_t stopping; - spinlock_t lock; - u32 update; }; struct isp_device; diff --git a/drivers/media/video/omap3isp/ispqueue.h b/drivers/media/video/omap3isp/ispqueue.h index 92c5a12157d5..908dfd712e8e 100644 --- a/drivers/media/video/omap3isp/ispqueue.h +++ b/drivers/media/video/omap3isp/ispqueue.h @@ -90,7 +90,7 @@ struct isp_video_buffer { void *vaddr; /* For userspace buffers. */ - unsigned long vm_flags; + vm_flags_t vm_flags; unsigned long offset; unsigned int npages; struct page **pages; diff --git a/drivers/media/video/omap3isp/ispresizer.c b/drivers/media/video/omap3isp/ispresizer.c index 6958a9e3dc22..14041c9c8643 100644 --- a/drivers/media/video/omap3isp/ispresizer.c +++ b/drivers/media/video/omap3isp/ispresizer.c @@ -1187,32 +1187,6 @@ static int resizer_set_stream(struct v4l2_subdev *sd, int enable) return 0; } -/* - * resizer_g_crop - handle get crop subdev operation - * @sd : pointer to v4l2 subdev structure - * @pad : subdev pad - * @crop : pointer to crop structure - * @which : active or try format - * return zero - */ -static int resizer_g_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) -{ - struct isp_res_device *res = v4l2_get_subdevdata(sd); - struct v4l2_mbus_framefmt *format; - struct resizer_ratio ratio; - - /* Only sink pad has crop capability */ - if (crop->pad != RESZ_PAD_SINK) - return -EINVAL; - - format = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, crop->which); - crop->rect = *__resizer_get_crop(res, fh, crop->which); - resizer_calc_ratios(res, &crop->rect, format, &ratio); - - return 0; -} - /* * resizer_try_crop - mangles crop parameters. */ @@ -1223,7 +1197,7 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, const unsigned int spv = DEFAULT_PHASE; const unsigned int sph = DEFAULT_PHASE; - /* Crop rectangle is constrained to the output size so that zoom ratio + /* Crop rectangle is constrained by the output size so that zoom ratio * cannot exceed +/-4.0. */ unsigned int min_width = @@ -1248,51 +1222,115 @@ static void resizer_try_crop(const struct v4l2_mbus_framefmt *sink, } /* - * resizer_s_crop - handle set crop subdev operation - * @sd : pointer to v4l2 subdev structure - * @pad : subdev pad - * @crop : pointer to crop structure - * @which : active or try format - * return -EINVAL or zero when succeed + * resizer_get_selection - Retrieve a selection rectangle on a pad + * @sd: ISP resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangles are the crop rectangles on the sink pad. + * + * Return 0 on success or a negative error code otherwise. */ -static int resizer_s_crop(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int resizer_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct isp_res_device *res = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *format_source; + struct v4l2_mbus_framefmt *format_sink; + struct resizer_ratio ratio; + + if (sel->pad != RESZ_PAD_SINK) + return -EINVAL; + + format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, + sel->which); + format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, + sel->which); + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = INT_MAX; + sel->r.height = INT_MAX; + + resizer_try_crop(format_sink, format_source, &sel->r); + resizer_calc_ratios(res, &sel->r, format_source, &ratio); + break; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + sel->r = *__resizer_get_crop(res, fh, sel->which); + resizer_calc_ratios(res, &sel->r, format_source, &ratio); + break; + + default: + return -EINVAL; + } + + return 0; +} + +/* + * resizer_set_selection - Set a selection rectangle on a pad + * @sd: ISP resizer V4L2 subdevice + * @fh: V4L2 subdev file handle + * @sel: Selection rectangle + * + * The only supported rectangle is the actual crop rectangle on the sink pad. + * + * FIXME: This function currently behaves as if the KEEP_CONFIG selection flag + * was always set. + * + * Return 0 on success or a negative error code otherwise. + */ +static int resizer_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct isp_res_device *res = v4l2_get_subdevdata(sd); struct isp_device *isp = to_isp_device(res); struct v4l2_mbus_framefmt *format_sink, *format_source; struct resizer_ratio ratio; - /* Only sink pad has crop capability */ - if (crop->pad != RESZ_PAD_SINK) + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != RESZ_PAD_SINK) return -EINVAL; format_sink = __resizer_get_format(res, fh, RESZ_PAD_SINK, - crop->which); + sel->which); format_source = __resizer_get_format(res, fh, RESZ_PAD_SOURCE, - crop->which); + sel->which); dev_dbg(isp->dev, "%s: L=%d,T=%d,W=%d,H=%d,which=%d\n", __func__, - crop->rect.left, crop->rect.top, crop->rect.width, - crop->rect.height, crop->which); + sel->r.left, sel->r.top, sel->r.width, sel->r.height, + sel->which); dev_dbg(isp->dev, "%s: input=%dx%d, output=%dx%d\n", __func__, format_sink->width, format_sink->height, format_source->width, format_source->height); - resizer_try_crop(format_sink, format_source, &crop->rect); - *__resizer_get_crop(res, fh, crop->which) = crop->rect; - resizer_calc_ratios(res, &crop->rect, format_source, &ratio); + /* Clamp the crop rectangle to the bounds, and then mangle it further to + * fulfill the TRM equations. Store the clamped but otherwise unmangled + * rectangle to avoid cropping the input multiple times: when an + * application sets the output format, the current crop rectangle is + * mangled during crop rectangle computation, which would lead to a new, + * smaller input crop rectangle every time the output size is set if we + * stored the mangled rectangle. + */ + resizer_try_crop(format_sink, format_source, &sel->r); + *__resizer_get_crop(res, fh, sel->which) = sel->r; + resizer_calc_ratios(res, &sel->r, format_source, &ratio); - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) return 0; res->ratio = ratio; - res->crop.active = crop->rect; + res->crop.active = sel->r; /* - * s_crop can be called while streaming is on. In this case - * the crop values will be set in the next IRQ. + * set_selection can be called while streaming is on. In this case the + * crop values will be set in the next IRQ. */ if (res->state != ISP_PIPELINE_STREAM_STOPPED) res->applycrop = 1; @@ -1530,8 +1568,8 @@ static const struct v4l2_subdev_pad_ops resizer_v4l2_pad_ops = { .enum_frame_size = resizer_enum_frame_size, .get_fmt = resizer_get_format, .set_fmt = resizer_set_format, - .get_crop = resizer_g_crop, - .set_crop = resizer_s_crop, + .get_selection = resizer_get_selection, + .set_selection = resizer_set_selection, }; /* subdev operations */ @@ -1603,6 +1641,7 @@ static int resizer_link_setup(struct media_entity *entity, /* media operations */ static const struct media_entity_operations resizer_media_ops = { .link_setup = resizer_link_setup, + .link_validate = v4l2_subdev_link_validate, }; void omap3isp_resizer_unregister_entities(struct isp_res_device *res) diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c index 11871ecc6d25..b8640be692f1 100644 --- a/drivers/media/video/omap3isp/ispstat.c +++ b/drivers/media/video/omap3isp/ispstat.c @@ -1032,7 +1032,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, if (sub->type != stat->event_type) return -EINVAL; - return v4l2_event_subscribe(fh, sub, STAT_NEVENTS); + return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL); } int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, diff --git a/drivers/media/video/omap3isp/ispvideo.c b/drivers/media/video/omap3isp/ispvideo.c index b02070057724..b37379d39cdd 100644 --- a/drivers/media/video/omap3isp/ispvideo.c +++ b/drivers/media/video/omap3isp/ispvideo.c @@ -46,6 +46,10 @@ * Helper functions */ +/* + * NOTE: When adding new media bus codes, always remember to add + * corresponding in-memory formats to the table below!!! + */ static struct isp_format_info formats[] = { { V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, V4L2_MBUS_FMT_Y8_1X8, @@ -68,9 +72,18 @@ static struct isp_format_info formats[] = { { V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_MBUS_FMT_SRGGB8_1X8, V4L2_PIX_FMT_SRGGB8, 8, }, + { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, + V4L2_MBUS_FMT_SBGGR10_1X10, 0, + V4L2_PIX_FMT_SBGGR10DPCM8, 8, }, + { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, + V4L2_MBUS_FMT_SGBRG10_1X10, 0, + V4L2_PIX_FMT_SGBRG10DPCM8, 8, }, { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, V4L2_MBUS_FMT_SGRBG10_1X10, 0, V4L2_PIX_FMT_SGRBG10DPCM8, 8, }, + { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, + V4L2_MBUS_FMT_SRGGB10_1X10, 0, + V4L2_PIX_FMT_SRGGB10DPCM8, 8, }, { V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR10_1X10, V4L2_MBUS_FMT_SBGGR8_1X8, V4L2_PIX_FMT_SBGGR10, 10, }, @@ -116,37 +129,6 @@ omap3isp_video_format_info(enum v4l2_mbus_pixelcode code) return NULL; } -/* - * Decide whether desired output pixel code can be obtained with - * the lane shifter by shifting the input pixel code. - * @in: input pixelcode to shifter - * @out: output pixelcode from shifter - * @additional_shift: # of bits the sensor's LSB is offset from CAMEXT[0] - * - * return true if the combination is possible - * return false otherwise - */ -static bool isp_video_is_shiftable(enum v4l2_mbus_pixelcode in, - enum v4l2_mbus_pixelcode out, - unsigned int additional_shift) -{ - const struct isp_format_info *in_info, *out_info; - - if (in == out) - return true; - - in_info = omap3isp_video_format_info(in); - out_info = omap3isp_video_format_info(out); - - if ((in_info->flavor == 0) || (out_info->flavor == 0)) - return false; - - if (in_info->flavor != out_info->flavor) - return false; - - return in_info->bpp - out_info->bpp + additional_shift <= 6; -} - /* * isp_video_mbus_to_pix - Convert v4l2_mbus_framefmt to v4l2_pix_format * @video: ISP video instance @@ -242,8 +224,8 @@ isp_video_remote_subdev(struct isp_video *video, u32 *pad) } /* Return a pointer to the ISP video instance at the far end of the pipeline. */ -static struct isp_video * -isp_video_far_end(struct isp_video *video) +static int isp_video_get_graph_data(struct isp_video *video, + struct isp_pipeline *pipe) { struct media_entity_graph graph; struct media_entity *entity = &video->video.entity; @@ -254,21 +236,38 @@ isp_video_far_end(struct isp_video *video) media_entity_graph_walk_start(&graph, entity); while ((entity = media_entity_graph_walk_next(&graph))) { + struct isp_video *__video; + + pipe->entities |= 1 << entity->id; + + if (far_end != NULL) + continue; + if (entity == &video->video.entity) continue; if (media_entity_type(entity) != MEDIA_ENT_T_DEVNODE) continue; - far_end = to_isp_video(media_entity_to_video_device(entity)); - if (far_end->type != video->type) - break; - - far_end = NULL; + __video = to_isp_video(media_entity_to_video_device(entity)); + if (__video->type != video->type) + far_end = __video; } mutex_unlock(&mdev->graph_mutex); - return far_end; + + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + pipe->input = far_end; + pipe->output = video; + } else { + if (far_end == NULL) + return -EPIPE; + + pipe->input = video; + pipe->output = far_end; + } + + return 0; } /* @@ -285,52 +284,24 @@ isp_video_far_end(struct isp_video *video) static int isp_video_validate_pipeline(struct isp_pipeline *pipe) { struct isp_device *isp = pipe->output->isp; - struct v4l2_subdev_format fmt_source; - struct v4l2_subdev_format fmt_sink; struct media_pad *pad; struct v4l2_subdev *subdev; - int ret; - - pipe->max_rate = pipe->l3_ick; subdev = isp_video_remote_subdev(pipe->output, NULL); if (subdev == NULL) return -EPIPE; while (1) { - unsigned int shifter_link; /* Retrieve the sink format */ pad = &subdev->entity.pads[0]; if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; - fmt_sink.pad = pad->index; - fmt_sink.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_sink); - if (ret < 0 && ret != -ENOIOCTLCMD) - return -EPIPE; - /* Update the maximum frame rate */ if (subdev == &isp->isp_res.subdev) omap3isp_resizer_max_rate(&isp->isp_res, &pipe->max_rate); - /* Check ccdc maximum data rate when data comes from sensor - * TODO: Include ccdc rate in pipe->max_rate and compare the - * total pipe rate with the input data rate from sensor. - */ - if (subdev == &isp->isp_ccdc.subdev && pipe->input == NULL) { - unsigned int rate = UINT_MAX; - - omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); - if (isp->isp_ccdc.vpcfg.pixelclk > rate) - return -ENOSPC; - } - - /* If sink pad is on CCDC, the link has the lane shifter - * in the middle of it. */ - shifter_link = subdev == &isp->isp_ccdc.subdev; - /* Retrieve the source format. Return an error if no source * entity can be found, and stop checking the pipeline if the * source entity isn't a subdev. @@ -343,32 +314,6 @@ static int isp_video_validate_pipeline(struct isp_pipeline *pipe) break; subdev = media_entity_to_v4l2_subdev(pad->entity); - - fmt_source.pad = pad->index; - fmt_source.which = V4L2_SUBDEV_FORMAT_ACTIVE; - ret = v4l2_subdev_call(subdev, pad, get_fmt, NULL, &fmt_source); - if (ret < 0 && ret != -ENOIOCTLCMD) - return -EPIPE; - - /* Check if the two ends match */ - if (fmt_source.format.width != fmt_sink.format.width || - fmt_source.format.height != fmt_sink.format.height) - return -EPIPE; - - if (shifter_link) { - unsigned int parallel_shift = 0; - if (isp->isp_ccdc.input == CCDC_INPUT_PARALLEL) { - struct isp_parallel_platform_data *pdata = - &((struct isp_v4l2_subdevs_group *) - subdev->host_priv)->bus.parallel; - parallel_shift = pdata->data_lane_shift * 2; - } - if (!isp_video_is_shiftable(fmt_source.format.code, - fmt_sink.format.code, - parallel_shift)) - return -EPIPE; - } else if (fmt_source.format.code != fmt_sink.format.code) - return -EPIPE; } return 0; @@ -923,6 +868,92 @@ isp_video_dqbuf(struct file *file, void *fh, struct v4l2_buffer *b) file->f_flags & O_NONBLOCK); } +static int isp_video_check_external_subdevs(struct isp_video *video, + struct isp_pipeline *pipe) +{ + struct isp_device *isp = video->isp; + struct media_entity *ents[] = { + &isp->isp_csi2a.subdev.entity, + &isp->isp_csi2c.subdev.entity, + &isp->isp_ccp2.subdev.entity, + &isp->isp_ccdc.subdev.entity + }; + struct media_pad *source_pad; + struct media_entity *source = NULL; + struct media_entity *sink; + struct v4l2_subdev_format fmt; + struct v4l2_ext_controls ctrls; + struct v4l2_ext_control ctrl; + unsigned int i; + int ret = 0; + + for (i = 0; i < ARRAY_SIZE(ents); i++) { + /* Is the entity part of the pipeline? */ + if (!(pipe->entities & (1 << ents[i]->id))) + continue; + + /* ISP entities have always sink pad == 0. Find source. */ + source_pad = media_entity_remote_source(&ents[i]->pads[0]); + if (source_pad == NULL) + continue; + + source = source_pad->entity; + sink = ents[i]; + break; + } + + if (!source) { + dev_warn(isp->dev, "can't find source, failing now\n"); + return ret; + } + + if (media_entity_type(source) != MEDIA_ENT_T_V4L2_SUBDEV) + return 0; + + pipe->external = media_entity_to_v4l2_subdev(source); + + fmt.pad = source_pad->index; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(media_entity_to_v4l2_subdev(sink), + pad, get_fmt, NULL, &fmt); + if (unlikely(ret < 0)) { + dev_warn(isp->dev, "get_fmt returned null!\n"); + return ret; + } + + pipe->external_bpp = omap3isp_video_format_info(fmt.format.code)->bpp; + + memset(&ctrls, 0, sizeof(ctrls)); + memset(&ctrl, 0, sizeof(ctrl)); + + ctrl.id = V4L2_CID_PIXEL_RATE; + + ctrls.count = 1; + ctrls.controls = &ctrl; + + ret = v4l2_g_ext_ctrls(pipe->external->ctrl_handler, &ctrls); + if (ret < 0) { + dev_warn(isp->dev, "no pixel rate control in subdev %s\n", + pipe->external->name); + return ret; + } + + pipe->external_rate = ctrl.value64; + + if (pipe->entities & (1 << isp->isp_ccdc.subdev.entity.id)) { + unsigned int rate = UINT_MAX; + /* + * Check that maximum allowed CCDC pixel rate isn't + * exceeded by the pixel rate. + */ + omap3isp_ccdc_max_rate(&isp->isp_ccdc, &rate); + if (pipe->external_rate > rate) + return -ENOSPC; + } + + return 0; +} + /* * Stream management * @@ -961,7 +992,6 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) struct isp_video *video = video_drvdata(file); enum isp_pipeline_state state; struct isp_pipeline *pipe; - struct isp_video *far_end; unsigned long flags; int ret; @@ -980,46 +1010,45 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) */ pipe = video->video.entity.pipe ? to_isp_pipeline(&video->video.entity) : &video->pipe; - media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + + pipe->entities = 0; + + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, true); + pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); + pipe->max_rate = pipe->l3_ick; + + ret = media_entity_pipeline_start(&video->video.entity, &pipe->pipe); + if (ret < 0) + goto err_pipeline_start; /* Verify that the currently configured format matches the output of * the connected subdev. */ ret = isp_video_check_format(video, vfh); if (ret < 0) - goto error; + goto err_check_format; video->bpl_padding = ret; video->bpl_value = vfh->format.fmt.pix.bytesperline; - /* Find the ISP video node connected at the far end of the pipeline and - * update the pipeline. - */ - far_end = isp_video_far_end(video); + ret = isp_video_get_graph_data(video, pipe); + if (ret < 0) + goto err_check_format; - if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (video->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) state = ISP_PIPELINE_STREAM_OUTPUT | ISP_PIPELINE_IDLE_OUTPUT; - pipe->input = far_end; - pipe->output = video; - } else { - if (far_end == NULL) { - ret = -EPIPE; - goto error; - } - + else state = ISP_PIPELINE_STREAM_INPUT | ISP_PIPELINE_IDLE_INPUT; - pipe->input = video; - pipe->output = far_end; - } - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, true); - pipe->l3_ick = clk_get_rate(video->isp->clock[ISP_CLK_L3_ICK]); + ret = isp_video_check_external_subdevs(video, pipe); + if (ret < 0) + goto err_check_format; /* Validate the pipeline and update its state. */ ret = isp_video_validate_pipeline(pipe); if (ret < 0) - goto error; + goto err_check_format; pipe->error = false; @@ -1041,7 +1070,7 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) ret = omap3isp_video_queue_streamon(&vfh->queue); if (ret < 0) - goto error; + goto err_check_format; /* In sensor-to-memory mode, the stream can be started synchronously * to the stream on command. In memory-to-memory mode, it will be @@ -1051,32 +1080,34 @@ isp_video_streamon(struct file *file, void *fh, enum v4l2_buf_type type) ret = omap3isp_pipeline_set_stream(pipe, ISP_PIPELINE_STREAM_CONTINUOUS); if (ret < 0) - goto error; + goto err_set_stream; spin_lock_irqsave(&video->queue->irqlock, flags); if (list_empty(&video->dmaqueue)) video->dmaqueue_flags |= ISP_VIDEO_DMAQUEUE_UNDERRUN; spin_unlock_irqrestore(&video->queue->irqlock, flags); } -error: - if (ret < 0) { - omap3isp_video_queue_streamoff(&vfh->queue); - if (video->isp->pdata->set_constraints) - video->isp->pdata->set_constraints(video->isp, false); - media_entity_pipeline_stop(&video->video.entity); - /* The DMA queue must be emptied here, otherwise CCDC interrupts - * that will get triggered the next time the CCDC is powered up - * will try to access buffers that might have been freed but - * still present in the DMA queue. This can easily get triggered - * if the above omap3isp_pipeline_set_stream() call fails on a - * system with a free-running sensor. - */ - INIT_LIST_HEAD(&video->dmaqueue); - video->queue = NULL; - } + video->streaming = 1; - if (!ret) - video->streaming = 1; + mutex_unlock(&video->stream_lock); + return 0; + +err_set_stream: + omap3isp_video_queue_streamoff(&vfh->queue); +err_check_format: + media_entity_pipeline_stop(&video->video.entity); +err_pipeline_start: + if (video->isp->pdata->set_constraints) + video->isp->pdata->set_constraints(video->isp, false); + /* The DMA queue must be emptied here, otherwise CCDC interrupts that + * will get triggered the next time the CCDC is powered up will try to + * access buffers that might have been freed but still present in the + * DMA queue. This can easily get triggered if the above + * omap3isp_pipeline_set_stream() call fails on a system with a + * free-running sensor. + */ + INIT_LIST_HEAD(&video->dmaqueue); + video->queue = NULL; mutex_unlock(&video->stream_lock); return ret; diff --git a/drivers/media/video/omap3isp/ispvideo.h b/drivers/media/video/omap3isp/ispvideo.h index d91bdb919be0..5acc909500ec 100644 --- a/drivers/media/video/omap3isp/ispvideo.h +++ b/drivers/media/video/omap3isp/ispvideo.h @@ -88,6 +88,7 @@ enum isp_pipeline_state { /* * struct isp_pipeline - An ISP hardware pipeline * @error: A hardware error occurred during capture + * @entities: Bitmask of entities in the pipeline (indexed by entity ID) */ struct isp_pipeline { struct media_pipeline pipe; @@ -96,12 +97,16 @@ struct isp_pipeline { enum isp_pipeline_stream_state stream_state; struct isp_video *input; struct isp_video *output; + u32 entities; unsigned long l3_ick; unsigned int max_rate; atomic_t frame_number; bool do_propagation; /* of frame number */ bool error; struct v4l2_fract max_timeperframe; + struct v4l2_subdev *external; + unsigned int external_rate; + unsigned int external_bpp; }; #define to_isp_pipeline(__e) \ diff --git a/drivers/media/video/ov5642.c b/drivers/media/video/ov5642.c index 80e07794ac8e..0bc93313d37a 100644 --- a/drivers/media/video/ov5642.c +++ b/drivers/media/video/ov5642.c @@ -1025,8 +1025,6 @@ static int ov5642_probe(struct i2c_client *client, priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; priv->crop_rect.left = (OV5642_MAX_WIDTH - OV5642_DEFAULT_WIDTH) / 2; priv->crop_rect.top = (OV5642_MAX_HEIGHT - OV5642_DEFAULT_HEIGHT) / 2; - priv->crop_rect.width = OV5642_DEFAULT_WIDTH; - priv->crop_rect.height = OV5642_DEFAULT_HEIGHT; priv->total_width = OV5642_DEFAULT_WIDTH + BLANKING_EXTRA_WIDTH; priv->total_height = BLANKING_MIN_HEIGHT; diff --git a/drivers/media/video/pms.c b/drivers/media/video/pms.c index e753b5e4d2ce..af2d9086d7e8 100644 --- a/drivers/media/video/pms.c +++ b/drivers/media/video/pms.c @@ -30,15 +30,19 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include #include MODULE_LICENSE("GPL"); -MODULE_VERSION("0.0.4"); +MODULE_VERSION("0.0.5"); #define MOTOROLA 1 #define PHILIPS2 2 /* SAA7191 */ @@ -55,11 +59,11 @@ struct i2c_info { struct pms { struct v4l2_device v4l2_dev; struct video_device vdev; + struct v4l2_ctrl_handler hdl; int height; int width; int depth; int input; - s32 brightness, saturation, hue, contrast; struct mutex lock; int i2c_count; struct i2c_info i2cinfo[64]; @@ -72,8 +76,6 @@ struct pms { void __iomem *mem; }; -static struct pms pms_card; - /* * I/O ports and Shared Memory */ @@ -676,8 +678,10 @@ static int pms_querycap(struct file *file, void *priv, 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->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + snprintf(vcap->bus_info, sizeof(vcap->bus_info), + "ISA:%s", dev->v4l2_dev.name); + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -716,11 +720,9 @@ static int pms_s_input(struct file *file, void *fh, unsigned int inp) 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; } @@ -738,7 +740,6 @@ static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) int ret = 0; dev->std = *std; - mutex_lock(&dev->lock); if (dev->std & V4L2_STD_NTSC) { pms_framerate(dev, 30); pms_secamcross(dev, 0); @@ -762,81 +763,31 @@ static int pms_s_std(struct file *file, void *fh, v4l2_std_id *std) 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) +static int pms_s_ctrl(struct v4l2_ctrl *ctrl) { - struct pms *dev = video_drvdata(file); + struct pms *dev = container_of(ctrl->handler, struct pms, hdl); int ret = 0; - mutex_lock(&dev->lock); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - dev->brightness = ctrl->value; - pms_brightness(dev, dev->brightness); + pms_brightness(dev, ctrl->val); break; case V4L2_CID_CONTRAST: - dev->contrast = ctrl->value; - pms_contrast(dev, dev->contrast); + pms_contrast(dev, ctrl->val); break; case V4L2_CID_SATURATION: - dev->saturation = ctrl->value; - pms_saturation(dev, dev->saturation); + pms_saturation(dev, ctrl->val); break; case V4L2_CID_HUE: - dev->hue = ctrl->value; - pms_hue(dev, dev->hue); + pms_hue(dev, ctrl->val); break; default: ret = -EINVAL; break; } - mutex_unlock(&dev->lock); return ret; } @@ -884,13 +835,11 @@ static int pms_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fm 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; } @@ -901,7 +850,7 @@ static int pms_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc "RGB 5:5:5", V4L2_PIX_FMT_RGB555, { 0, 0, 0, 0 } }, - { 0, 0, 0, + { 1, 0, 0, "RGB 5:6:5", V4L2_PIX_FMT_RGB565, { 0, 0, 0, 0 } }, @@ -922,32 +871,43 @@ static ssize_t pms_read(struct file *file, char __user *buf, struct pms *dev = video_drvdata(file); int len; - mutex_lock(&dev->lock); len = pms_capture(dev, buf, (dev->depth == 15), count); - mutex_unlock(&dev->lock); return len; } +static unsigned int pms_poll(struct file *file, struct poll_table_struct *wait) +{ + struct v4l2_fh *fh = file->private_data; + unsigned int res = POLLIN | POLLRDNORM; + + if (v4l2_event_pending(fh)) + res |= POLLPRI; + poll_wait(file, &fh->wait, wait); + return res; +} + static const struct v4l2_file_operations pms_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = pms_poll, .unlocked_ioctl = video_ioctl2, .read = pms_read, }; 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, + .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_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, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; /* @@ -956,7 +916,6 @@ static const struct v4l2_ioctl_ops pms_ioctl_ops = { static int init_mediavision(struct pms *dev) { - int id; int idec, decst; int i; static const unsigned char i2c_defs[] = { @@ -988,7 +947,6 @@ static int init_mediavision(struct pms *dev) outb(dev->io >> 4, 0x9a01); /* Set IO port */ - id = mvv_read(dev, 3); decst = pms_i2c_stat(dev, 0x43); if (decst != -1) @@ -1068,76 +1026,125 @@ static int enable; module_param(enable, int, 0); #endif -static int __init pms_init(void) +static const struct v4l2_ctrl_ops pms_ctrl_ops = { + .s_ctrl = pms_s_ctrl, +}; + +static int pms_probe(struct device *pdev, unsigned int card) { - struct pms *dev = &pms_card; - struct v4l2_device *v4l2_dev = &dev->v4l2_dev; + struct pms *dev; + struct v4l2_device *v4l2_dev; + struct v4l2_ctrl_handler *hdl; 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) { - v4l2_err(v4l2_dev, - "PMS: not enabled, use pms.enable=1 to probe\n"); + pr_err("PMS: not enabled, use pms.enable=1 to probe\n"); return -ENODEV; } #endif + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) + return -ENOMEM; + dev->decoder = PHILIPS2; dev->io = io_port; dev->data = io_port + 1; + v4l2_dev = &dev->v4l2_dev; + hdl = &dev->hdl; - if (init_mediavision(dev)) { - v4l2_err(v4l2_dev, "Board not found.\n"); - return -ENODEV; - } - - res = v4l2_device_register(NULL, v4l2_dev); + res = v4l2_device_register(pdev, v4l2_dev); if (res < 0) { v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); - return res; + goto free_dev; + } + v4l2_info(v4l2_dev, "Mediavision Pro Movie Studio driver 0.05\n"); + + res = init_mediavision(dev); + if (res) { + v4l2_err(v4l2_dev, "Board not found.\n"); + goto free_io; } + v4l2_ctrl_handler_init(hdl, 4); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 139); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_CONTRAST, 0, 255, 1, 70); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_SATURATION, 0, 255, 1, 64); + v4l2_ctrl_new_std(hdl, &pms_ctrl_ops, + V4L2_CID_HUE, 0, 255, 1, 0); + if (hdl->error) { + res = hdl->error; + goto free_hdl; + } + + mutex_init(&dev->lock); strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name)); dev->vdev.v4l2_dev = v4l2_dev; + dev->vdev.ctrl_handler = hdl; dev->vdev.fops = &pms_fops; dev->vdev.ioctl_ops = &pms_ioctl_ops; dev->vdev.release = video_device_release_empty; + dev->vdev.lock = &dev->lock; + dev->vdev.tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; + set_bit(V4L2_FL_USE_FH_PRIO, &dev->vdev.flags); 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; + dev->depth = 16; 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; - } + v4l2_ctrl_handler_setup(hdl); + res = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, video_nr); + if (res >= 0) + return 0; + +free_hdl: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&dev->v4l2_dev); +free_io: + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); +free_dev: + kfree(dev); + return res; +} + +static int pms_remove(struct device *pdev, unsigned int card) +{ + struct pms *dev = dev_get_drvdata(pdev); + + video_unregister_device(&dev->vdev); + v4l2_ctrl_handler_free(&dev->hdl); + release_region(dev->io, 3); + release_region(0x9a01, 1); + iounmap(dev->mem); return 0; } +static struct isa_driver pms_driver = { + .probe = pms_probe, + .remove = pms_remove, + .driver = { + .name = "pms", + }, +}; + +static int __init pms_init(void) +{ + return isa_register_driver(&pms_driver, 1); +} + static void __exit pms_exit(void) { - struct pms *dev = &pms_card; - - video_unregister_device(&dev->vdev); - release_region(dev->io, 3); - release_region(0x9a01, 1); - iounmap(dev->mem); + isa_unregister_driver(&pms_driver); } module_init(pms_init); diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h index 305e6aaa844a..036952f2a3cb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h @@ -317,18 +317,16 @@ struct pvr2_hdw { v4l2_std_id std_mask_eeprom; // Hardware supported selections v4l2_std_id std_mask_avail; // Which standards we may select from v4l2_std_id std_mask_cur; // Currently selected standard(s) - unsigned int std_enum_cnt; // # of enumerated standards int std_enum_cur; // selected standard enumeration value int std_dirty; // True if std_mask_cur has changed struct pvr2_ctl_info std_info_enum; struct pvr2_ctl_info std_info_avail; struct pvr2_ctl_info std_info_cur; - struct v4l2_standard *std_defs; - const char **std_enum_names; + struct pvr2_ctl_info std_info_detect; // Generated string names, one per actual V4L2 standard const char *std_mask_ptrs[32]; - char std_mask_names[32][10]; + char std_mask_names[32][16]; int unit_number; /* ID for driver instance */ unsigned long serial_number; /* ID for hardware itself */ diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c index ebc2c7e39233..fb828ba1dbbe 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c @@ -334,8 +334,6 @@ static void pvr2_hdw_state_log_state(struct pvr2_hdw *); static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl); static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw); static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw); -static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw); -static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw); static void pvr2_hdw_quiescent_timeout(unsigned long); static void pvr2_hdw_decoder_stabilization_timeout(unsigned long); static void pvr2_hdw_encoder_wait_timeout(unsigned long); @@ -346,7 +344,7 @@ static int pvr2_send_request_ex(struct pvr2_hdw *hdw, void *write_data,unsigned int write_len, void *read_data,unsigned int read_len); static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw); - +static v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw); static void trace_stbit(const char *name,int val) { @@ -840,6 +838,12 @@ static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp) return 0; } +static int ctrl_stddetect_get(struct pvr2_ctrl *cptr, int *vp) +{ + *vp = pvr2_hdw_get_detected_std(cptr->hdw); + return 0; +} + static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp) { *vp = cptr->hdw->std_mask_avail; @@ -854,8 +858,7 @@ static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v) ns = (ns & ~m) | (v & m); if (ns == hdw->std_mask_avail) return 0; hdw->std_mask_avail = ns; - pvr2_hdw_internal_set_std_avail(hdw); - pvr2_hdw_internal_find_stdenum(hdw); + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; return 0; } @@ -895,7 +898,6 @@ static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v) if (ns == hdw->std_mask_cur) return 0; hdw->std_mask_cur = ns; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return 0; } @@ -941,40 +943,6 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp) } -static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v) -{ - struct pvr2_hdw *hdw = cptr->hdw; - if (v < 0) return -EINVAL; - if (v > hdw->std_enum_cnt) return -EINVAL; - hdw->std_enum_cur = v; - if (!v) return 0; - v--; - if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0; - hdw->std_mask_cur = hdw->std_defs[v].id; - hdw->std_dirty = !0; - return 0; -} - - -static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp) -{ - *vp = cptr->hdw->std_enum_cur; - return 0; -} - - -static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr) -{ - return cptr->hdw->std_dirty != 0; -} - - -static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr) -{ - cptr->hdw->std_dirty = 0; -} - - #define DEFINT(vmin,vmax) \ .type = pvr2_ctl_int, \ .def.type_int.min_value = vmin, \ @@ -1293,15 +1261,14 @@ static const struct pvr2_ctl_info control_defs[] = { .sym_to_val = ctrl_std_sym_to_val, .type = pvr2_ctl_bitmask, },{ - .desc = "Video Standard Name", - .name = "video_standard", - .internal_id = PVR2_CID_STDENUM, + .desc = "Video Standards Detected Mask", + .name = "video_standard_mask_detected", + .internal_id = PVR2_CID_STDDETECT, .skip_init = !0, - .get_value = ctrl_stdenumcur_get, - .set_value = ctrl_stdenumcur_set, - .is_dirty = ctrl_stdenumcur_is_dirty, - .clear_dirty = ctrl_stdenumcur_clear_dirty, - .type = pvr2_ctl_enum, + .get_value = ctrl_stddetect_get, + .val_to_sym = ctrl_std_val_to_sym, + .sym_to_val = ctrl_std_sym_to_val, + .type = pvr2_ctl_bitmask, } }; @@ -1936,7 +1903,7 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) hdw->std_mask_avail |= std2; } - pvr2_hdw_internal_set_std_avail(hdw); + hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; if (std1) { bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1); @@ -1945,7 +1912,6 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) bcnt,buf); hdw->std_mask_cur = std1; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } if (std3) { @@ -1955,7 +1921,6 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) " (determined by device type): %.*s",bcnt,buf); hdw->std_mask_cur = std3; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } @@ -1975,24 +1940,10 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw) bcnt,buf); hdw->std_mask_cur = std_eeprom_maps[idx].std; hdw->std_dirty = !0; - pvr2_hdw_internal_find_stdenum(hdw); return; } } - if (hdw->std_enum_cnt > 1) { - // Autoselect the first listed standard - hdw->std_enum_cur = 1; - hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id; - hdw->std_dirty = !0; - pvr2_trace(PVR2_TRACE_STD, - "Initial video standard auto-selected to %s", - hdw->std_defs[hdw->std_enum_cur-1].name); - return; - } - - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "Unable to select a viable initial video standard"); } @@ -2594,14 +2545,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr->info = ciptr; } - // Initialize video standard enum dynamic control - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM); - if (cptr) { - memcpy(&hdw->std_info_enum,cptr->info, - sizeof(hdw->std_info_enum)); - cptr->info = &hdw->std_info_enum; - - } // Initialize control data regarding video standard masks valid_std_mask = pvr2_std_get_usable(); for (idx = 0; idx < 32; idx++) { @@ -2629,7 +2572,17 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, cptr->info = &hdw->std_info_cur; hdw->std_info_cur.def.type_bitmask.bit_names = hdw->std_mask_ptrs; - hdw->std_info_avail.def.type_bitmask.valid_bits = + hdw->std_info_cur.def.type_bitmask.valid_bits = + valid_std_mask; + } + cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDDETECT); + if (cptr) { + memcpy(&hdw->std_info_detect,cptr->info, + sizeof(hdw->std_info_detect)); + cptr->info = &hdw->std_info_detect; + hdw->std_info_detect.def.type_bitmask.bit_names = + hdw->std_mask_ptrs; + hdw->std_info_detect.def.type_bitmask.valid_bits = valid_std_mask; } @@ -2711,8 +2664,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf, kfree(hdw->ctl_write_buffer); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); - kfree(hdw->std_defs); - kfree(hdw->std_enum_names); kfree(hdw); } return NULL; @@ -2788,8 +2739,6 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw) } while (0); mutex_unlock(&pvr2_unit_mtx); kfree(hdw->controls); kfree(hdw->mpeg_ctrl_info); - kfree(hdw->std_defs); - kfree(hdw->std_enum_names); kfree(hdw); } @@ -2812,86 +2761,6 @@ void pvr2_hdw_disconnect(struct pvr2_hdw *hdw) } -// Attempt to autoselect an appropriate value for std_enum_cur given -// whatever is currently in std_mask_cur -static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw) -{ - unsigned int idx; - for (idx = 1; idx < hdw->std_enum_cnt; idx++) { - if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) { - hdw->std_enum_cur = idx; - return; - } - } - hdw->std_enum_cur = 0; -} - - -// Calculate correct set of enumerated standards based on currently known -// set of available standards bits. -static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw) -{ - struct v4l2_standard *newstd; - unsigned int std_cnt; - unsigned int idx; - - newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail); - - if (hdw->std_defs) { - kfree(hdw->std_defs); - hdw->std_defs = NULL; - } - hdw->std_enum_cnt = 0; - if (hdw->std_enum_names) { - kfree(hdw->std_enum_names); - hdw->std_enum_names = NULL; - } - - if (!std_cnt) { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "WARNING: Failed to identify any viable standards"); - } - - /* Set up the dynamic control for this standard */ - hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL); - if (hdw->std_enum_names) { - hdw->std_enum_names[0] = "none"; - for (idx = 0; idx < std_cnt; idx++) - hdw->std_enum_names[idx+1] = newstd[idx].name; - hdw->std_info_enum.def.type_enum.value_names = - hdw->std_enum_names; - hdw->std_info_enum.def.type_enum.count = std_cnt+1; - } else { - pvr2_trace( - PVR2_TRACE_ERROR_LEGS, - "WARNING: Failed to alloc memory for names"); - hdw->std_info_enum.def.type_enum.value_names = NULL; - hdw->std_info_enum.def.type_enum.count = 0; - } - hdw->std_defs = newstd; - hdw->std_enum_cnt = std_cnt+1; - hdw->std_enum_cur = 0; - hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail; -} - - -int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw, - struct v4l2_standard *std, - unsigned int idx) -{ - int ret = -EINVAL; - if (!idx) return ret; - LOCK_TAKE(hdw->big_lock); do { - if (idx >= hdw->std_enum_cnt) break; - idx--; - memcpy(std,hdw->std_defs+idx,sizeof(*std)); - ret = 0; - } while (0); LOCK_GIVE(hdw->big_lock); - return ret; -} - - /* Get the number of defined controls */ unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw) { @@ -2995,11 +2864,13 @@ static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id, pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \ } -int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std) +v4l2_std_id pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw) { + v4l2_std_id std; + std = (v4l2_std_id)hdw->std_mask_avail; v4l2_device_call_all(&hdw->v4l2_dev, 0, - video, querystd, std); - return 0; + video, querystd, &std); + return std; } /* Execute whatever commands are required to update the state of all the diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h index 66546580b17d..8060fc666eeb 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h +++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h @@ -28,7 +28,6 @@ /* Private internal control ids, look these up with pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */ -#define PVR2_CID_STDENUM 1 #define PVR2_CID_STDCUR 2 #define PVR2_CID_STDAVAIL 3 #define PVR2_CID_INPUT 4 @@ -46,6 +45,7 @@ #define PVR2_CID_CROPCAPBT 16 #define PVR2_CID_CROPCAPBW 17 #define PVR2_CID_CROPCAPBH 18 +#define PVR2_CID_STDDETECT 19 /* Legal values for the INPUT state variable */ #define PVR2_CVAL_INPUT_TV 0 @@ -210,13 +210,6 @@ int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config); /* Get handle to video output stream */ struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *); -/* Emit a video standard struct */ -int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std, - unsigned int idx); - -/* Get the detected video standard */ -int pvr2_hdw_get_detected_std(struct pvr2_hdw *hdw, v4l2_std_id *std); - /* Enable / disable retrieval of CPU firmware or prom contents. This must be enabled before pvr2_hdw_cpufw_get() will function. Note that doing this may prevent the device from running (and leaving this mode may diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c index e1111d968a3d..7bddfaeeafc3 100644 --- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c +++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c @@ -107,7 +107,6 @@ static struct v4l2_fmtdesc pvr_fmtdesc [] = { // This should really be V4L2_PIX_FMT_MPEG, but xawtv // breaks when I do that. .pixelformat = 0, // V4L2_PIX_FMT_MPEG, - .reserved = { 0, 0, 0, 0 } } }; @@ -145,741 +144,740 @@ static struct v4l2_format pvr_format [] = { .start = { 0, 0 }, .count = { 0, 0 }, .flags = 0, - .reserved = { 0, 0 } } } } }; + /* - * pvr_ioctl() - * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. - * + * This is part of Video 4 Linux API. These procedures handle ioctl() calls. */ -static long pvr2_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) +static int pvr2_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); + strlcpy(cap->bus_info, pvr2_hdw_get_bus_info(hdw), + sizeof(cap->bus_info)); + strlcpy(cap->card, pvr2_hdw_get_desc(hdw), sizeof(cap->card)); + return 0; +} + +static int pvr2_g_priority(struct file *file, void *priv, enum v4l2_priority *p) { struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_v4l2 *vp = fh->vhead; - struct pvr2_v4l2_dev *pdi = fh->pdi; + + *p = v4l2_prio_max(&vp->prio); + return 0; +} + +static int pvr2_s_priority(struct file *file, void *priv, enum v4l2_priority prio) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; + + return v4l2_prio_change(&vp->prio, &fh->prio, prio); +} + +static int pvr2_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - long ret = -EINVAL; + int val = 0; + int ret; - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd); - } + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), &val); + *std = val; + return ret; +} - if (!pvr2_hdw_dev_ok(hdw)) { - pvr2_trace(PVR2_TRACE_ERROR_LEGS, - "ioctl failed - bad or no context"); - return -EFAULT; - } +int pvr2_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; - /* check priority */ - switch (cmd) { - case VIDIOC_S_CTRL: - case VIDIOC_S_STD: - case VIDIOC_S_INPUT: - case VIDIOC_S_TUNER: - case VIDIOC_S_FREQUENCY: - ret = v4l2_prio_check(&vp->prio, fh->prio); - if (ret) - return ret; - } + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDCUR), *std); +} - switch (cmd) { - case VIDIOC_QUERYCAP: - { - struct v4l2_capability *cap = arg; +static int pvr2_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; - memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability)); - strlcpy(cap->bus_info,pvr2_hdw_get_bus_info(hdw), - sizeof(cap->bus_info)); - strlcpy(cap->card,pvr2_hdw_get_desc(hdw),sizeof(cap->card)); + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_STDDETECT), &val); + *std = val; + return ret; +} - ret = 0; +static int pvr2_enum_input(struct file *file, void *priv, struct v4l2_input *vi) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *cptr; + struct v4l2_input tmp; + unsigned int cnt; + int val; + int ret; + + cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + + memset(&tmp, 0, sizeof(tmp)); + tmp.index = vi->index; + ret = 0; + if (vi->index >= fh->input_cnt) + return -EINVAL; + val = fh->input_map[vi->index]; + switch (val) { + case PVR2_CVAL_INPUT_TV: + case PVR2_CVAL_INPUT_DTV: + case PVR2_CVAL_INPUT_RADIO: + tmp.type = V4L2_INPUT_TYPE_TUNER; break; - } - - case VIDIOC_G_PRIORITY: - { - enum v4l2_priority *p = arg; - - *p = v4l2_prio_max(&vp->prio); - ret = 0; + case PVR2_CVAL_INPUT_SVIDEO: + case PVR2_CVAL_INPUT_COMPOSITE: + tmp.type = V4L2_INPUT_TYPE_CAMERA; break; + default: + return -EINVAL; } - case VIDIOC_S_PRIORITY: - { - enum v4l2_priority *prio = arg; + cnt = 0; + pvr2_ctrl_get_valname(cptr, val, + tmp.name, sizeof(tmp.name) - 1, &cnt); + tmp.name[cnt] = 0; - ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio); - break; - } + /* Don't bother with audioset, since this driver currently + always switches the audio whenever the video is + switched. */ - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *vs = (struct v4l2_standard *)arg; - int idx = vs->index; - ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1); - break; - } + /* Handling std is a tougher problem. It doesn't make + sense in cases where a device might be multi-standard. + We could just copy out the current value for the + standard, but it can change over time. For now just + leave it zero. */ + *vi = tmp; + return 0; +} - case VIDIOC_QUERYSTD: - { - v4l2_std_id *std = arg; - *std = V4L2_STD_ALL; - ret = pvr2_hdw_get_detected_std(hdw, std); - break; - } +static int pvr2_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned int idx; + struct pvr2_ctrl *cptr; + int val; + int ret; - case VIDIOC_G_STD: - { - int val = 0; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val); - *(v4l2_std_id *)arg = val; - break; - } - - case VIDIOC_S_STD: - { - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR), - *(v4l2_std_id *)arg); - break; - } - - case VIDIOC_ENUMINPUT: - { - struct pvr2_ctrl *cptr; - struct v4l2_input *vi = (struct v4l2_input *)arg; - struct v4l2_input tmp; - unsigned int cnt; - int val; - - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - - memset(&tmp,0,sizeof(tmp)); - tmp.index = vi->index; - ret = 0; - if (vi->index >= fh->input_cnt) { - ret = -EINVAL; + cptr = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + val = 0; + ret = pvr2_ctrl_get_value(cptr, &val); + *i = 0; + for (idx = 0; idx < fh->input_cnt; idx++) { + if (fh->input_map[idx] == val) { + *i = idx; break; } - val = fh->input_map[vi->index]; - switch (val) { - case PVR2_CVAL_INPUT_TV: - case PVR2_CVAL_INPUT_DTV: - case PVR2_CVAL_INPUT_RADIO: - tmp.type = V4L2_INPUT_TYPE_TUNER; - break; - case PVR2_CVAL_INPUT_SVIDEO: - case PVR2_CVAL_INPUT_COMPOSITE: - tmp.type = V4L2_INPUT_TYPE_CAMERA; - break; - default: - ret = -EINVAL; - break; - } - if (ret < 0) break; - - cnt = 0; - pvr2_ctrl_get_valname(cptr,val, - tmp.name,sizeof(tmp.name)-1,&cnt); - tmp.name[cnt] = 0; - - /* Don't bother with audioset, since this driver currently - always switches the audio whenever the video is - switched. */ - - /* Handling std is a tougher problem. It doesn't make - sense in cases where a device might be multi-standard. - We could just copy out the current value for the - standard, but it can change over time. For now just - leave it zero. */ - - memcpy(vi, &tmp, sizeof(tmp)); - - ret = 0; - break; - } - - case VIDIOC_G_INPUT: - { - unsigned int idx; - struct pvr2_ctrl *cptr; - struct v4l2_input *vi = (struct v4l2_input *)arg; - int val; - cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - val = 0; - ret = pvr2_ctrl_get_value(cptr,&val); - vi->index = 0; - for (idx = 0; idx < fh->input_cnt; idx++) { - if (fh->input_map[idx] == val) { - vi->index = idx; - break; - } - } - break; - } - - case VIDIOC_S_INPUT: - { - struct v4l2_input *vi = (struct v4l2_input *)arg; - if (vi->index >= fh->input_cnt) { - ret = -ERANGE; - break; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), - fh->input_map[vi->index]); - break; - } - - case VIDIOC_ENUMAUDIO: - { - /* pkt: FIXME: We are returning one "fake" input here - which could very well be called "whatever_we_like". - This is for apps that want to see an audio input - just to feel comfortable, as well as to test if - it can do stereo or sth. There is actually no guarantee - that the actual audio input cannot change behind the app's - back, but most applications should not mind that either. - - Hopefully, mplayer people will work with us on this (this - whole mess is to support mplayer pvr://), or Hans will come - up with a more standard way to say "we have inputs but we - don 't want you to change them independent of video" which - will sort this mess. - */ - struct v4l2_audio *vin = arg; - ret = -EINVAL; - if (vin->index > 0) break; - strncpy(vin->name, "PVRUSB2 Audio",14); - vin->capability = V4L2_AUDCAP_STEREO; - ret = 0; - break; - break; - } - - case VIDIOC_G_AUDIO: - { - /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ - struct v4l2_audio *vin = arg; - memset(vin,0,sizeof(*vin)); - vin->index = 0; - strncpy(vin->name, "PVRUSB2 Audio",14); - vin->capability = V4L2_AUDCAP_STEREO; - ret = 0; - break; - } - - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *vt = (struct v4l2_tuner *)arg; - - if (vt->index != 0) break; /* Only answer for the 1st tuner */ - - pvr2_hdw_execute_tuner_poll(hdw); - ret = pvr2_hdw_get_tuner_status(hdw,vt); - break; - } - - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *vt=(struct v4l2_tuner *)arg; - - if (vt->index != 0) - break; - - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE), - vt->audmode); - break; - } - - case VIDIOC_S_FREQUENCY: - { - const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - unsigned long fv; - struct v4l2_tuner vt; - int cur_input; - struct pvr2_ctrl *ctrlp; - ret = pvr2_hdw_get_tuner_status(hdw,&vt); - if (ret != 0) break; - ctrlp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT); - ret = pvr2_ctrl_get_value(ctrlp,&cur_input); - if (ret != 0) break; - if (vf->type == V4L2_TUNER_RADIO) { - if (cur_input != PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(ctrlp, - PVR2_CVAL_INPUT_RADIO); - } - } else { - if (cur_input == PVR2_CVAL_INPUT_RADIO) { - pvr2_ctrl_set_value(ctrlp, - PVR2_CVAL_INPUT_TV); - } - } - fv = vf->frequency; - if (vt.capability & V4L2_TUNER_CAP_LOW) { - fv = (fv * 125) / 2; - } else { - fv = fv * 62500; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); - break; - } - - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *vf = (struct v4l2_frequency *)arg; - int val = 0; - int cur_input; - struct v4l2_tuner vt; - ret = pvr2_hdw_get_tuner_status(hdw,&vt); - if (ret != 0) break; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY), - &val); - if (ret != 0) break; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT), - &cur_input); - if (cur_input == PVR2_CVAL_INPUT_RADIO) { - vf->type = V4L2_TUNER_RADIO; - } else { - vf->type = V4L2_TUNER_ANALOG_TV; - } - if (vt.capability & V4L2_TUNER_CAP_LOW) { - val = (val * 2) / 125; - } else { - val /= 62500; - } - vf->frequency = val; - break; - } - - case VIDIOC_ENUM_FMT: - { - struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg; - - /* Only one format is supported : mpeg.*/ - if (fd->index != 0) - break; - - memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); - ret = 0; - break; - } - - case VIDIOC_G_FMT: - { - struct v4l2_format *vf = (struct v4l2_format *)arg; - int val; - switch(vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: - memcpy(vf, &pvr_format[PVR_FORMAT_PIX], - sizeof(struct v4l2_format)); - val = 0; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES), - &val); - vf->fmt.pix.width = val; - val = 0; - pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES), - &val); - vf->fmt.pix.height = val; - ret = 0; - break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - // ????? Still need to figure out to do VBI correctly - ret = -EINVAL; - break; - default: - ret = -EINVAL; - break; - } - break; - } - - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: - { - struct v4l2_format *vf = (struct v4l2_format *)arg; - - ret = 0; - switch(vf->type) { - case V4L2_BUF_TYPE_VIDEO_CAPTURE: { - int lmin,lmax,ldef; - struct pvr2_ctrl *hcp,*vcp; - int h = vf->fmt.pix.height; - int w = vf->fmt.pix.width; - hcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES); - vcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES); - - lmin = pvr2_ctrl_get_min(hcp); - lmax = pvr2_ctrl_get_max(hcp); - pvr2_ctrl_get_def(hcp, &ldef); - if (w == -1) { - w = ldef; - } else if (w < lmin) { - w = lmin; - } else if (w > lmax) { - w = lmax; - } - lmin = pvr2_ctrl_get_min(vcp); - lmax = pvr2_ctrl_get_max(vcp); - pvr2_ctrl_get_def(vcp, &ldef); - if (h == -1) { - h = ldef; - } else if (h < lmin) { - h = lmin; - } else if (h > lmax) { - h = lmax; - } - - memcpy(vf, &pvr_format[PVR_FORMAT_PIX], - sizeof(struct v4l2_format)); - vf->fmt.pix.width = w; - vf->fmt.pix.height = h; - - if (cmd == VIDIOC_S_FMT) { - pvr2_ctrl_set_value(hcp,vf->fmt.pix.width); - pvr2_ctrl_set_value(vcp,vf->fmt.pix.height); - } - } break; - case V4L2_BUF_TYPE_VBI_CAPTURE: - // ????? Still need to figure out to do VBI correctly - ret = -EINVAL; - break; - default: - ret = -EINVAL; - break; - } - break; - } - - case VIDIOC_STREAMON: - { - if (!fh->pdi->stream) { - /* No stream defined for this node. This means - that we're not currently allowed to stream from - this node. */ - ret = -EPERM; - break; - } - ret = pvr2_hdw_set_stream_type(hdw,pdi->config); - if (ret < 0) return ret; - ret = pvr2_hdw_set_streaming(hdw,!0); - break; - } - - case VIDIOC_STREAMOFF: - { - if (!fh->pdi->stream) { - /* No stream defined for this node. This means - that we're not currently allowed to stream from - this node. */ - ret = -EPERM; - break; - } - ret = pvr2_hdw_set_streaming(hdw,0); - break; - } - - case VIDIOC_QUERYCTRL: - { - struct pvr2_ctrl *cptr; - int val; - struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg; - ret = 0; - if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { - cptr = pvr2_hdw_get_ctrl_nextv4l( - hdw,(vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); - if (cptr) vc->id = pvr2_ctrl_get_v4lid(cptr); - } else { - cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id); - } - if (!cptr) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x not implemented here", - vc->id); - ret = -EINVAL; - break; - } - - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x mapping name=%s (%s)", - vc->id,pvr2_ctrl_get_name(cptr), - pvr2_ctrl_get_desc(cptr)); - strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name)); - vc->flags = pvr2_ctrl_get_v4lflags(cptr); - pvr2_ctrl_get_def(cptr, &val); - vc->default_value = val; - switch (pvr2_ctrl_get_type(cptr)) { - case pvr2_ctl_enum: - vc->type = V4L2_CTRL_TYPE_MENU; - vc->minimum = 0; - vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; - vc->step = 1; - break; - case pvr2_ctl_bool: - vc->type = V4L2_CTRL_TYPE_BOOLEAN; - vc->minimum = 0; - vc->maximum = 1; - vc->step = 1; - break; - case pvr2_ctl_int: - vc->type = V4L2_CTRL_TYPE_INTEGER; - vc->minimum = pvr2_ctrl_get_min(cptr); - vc->maximum = pvr2_ctrl_get_max(cptr); - vc->step = 1; - break; - default: - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "QUERYCTRL id=0x%x name=%s not mappable", - vc->id,pvr2_ctrl_get_name(cptr)); - ret = -EINVAL; - break; - } - break; - } - - case VIDIOC_QUERYMENU: - { - struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg; - unsigned int cnt = 0; - ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id), - vm->index, - vm->name,sizeof(vm->name)-1, - &cnt); - vm->name[cnt] = 0; - break; - } - - case VIDIOC_G_CTRL: - { - struct v4l2_control *vc = (struct v4l2_control *)arg; - int val = 0; - ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), - &val); - vc->value = val; - break; - } - - case VIDIOC_S_CTRL: - { - struct v4l2_control *vc = (struct v4l2_control *)arg; - ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id), - vc->value); - break; - } - - case VIDIOC_G_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - unsigned int idx; - int val; - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),&val); - if (ret) { - ctls->error_idx = idx; - break; - } - /* Ensure that if read as a 64 bit value, the user - will still get a hopefully sane value */ - ctrl->value64 = 0; - ctrl->value = val; - } - break; - } - - case VIDIOC_S_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - unsigned int idx; - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id), - ctrl->value); - if (ret) { - ctls->error_idx = idx; - break; - } - } - break; - } - - case VIDIOC_TRY_EXT_CTRLS: - { - struct v4l2_ext_controls *ctls = - (struct v4l2_ext_controls *)arg; - struct v4l2_ext_control *ctrl; - struct pvr2_ctrl *pctl; - unsigned int idx; - /* For the moment just validate that the requested control - actually exists. */ - ret = 0; - for (idx = 0; idx < ctls->count; idx++) { - ctrl = ctls->controls + idx; - pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id); - if (!pctl) { - ret = -EINVAL; - ctls->error_idx = idx; - break; - } - } - break; - } - - case VIDIOC_CROPCAP: - { - struct v4l2_cropcap *cap = (struct v4l2_cropcap *)arg; - if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; - } - ret = pvr2_hdw_get_cropcap(hdw, cap); - cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ - break; - } - case VIDIOC_G_CROP: - { - struct v4l2_crop *crop = (struct v4l2_crop *)arg; - int val = 0; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.left = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.top = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.width = val; - ret = pvr2_ctrl_get_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); - if (ret != 0) { - ret = -EINVAL; - break; - } - crop->c.height = val; - } - case VIDIOC_S_CROP: - { - struct v4l2_crop *crop = (struct v4l2_crop *)arg; - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), - crop->c.left); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), - crop->c.top); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), - crop->c.width); - if (ret != 0) { - ret = -EINVAL; - break; - } - ret = pvr2_ctrl_set_value( - pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), - crop->c.height); - if (ret != 0) { - ret = -EINVAL; - break; - } - } - case VIDIOC_LOG_STATUS: - { - pvr2_hdw_trigger_module_log(hdw); - ret = 0; - break; - } -#ifdef CONFIG_VIDEO_ADV_DEBUG - case VIDIOC_DBG_S_REGISTER: - case VIDIOC_DBG_G_REGISTER: - { - u64 val; - struct v4l2_dbg_register *req = (struct v4l2_dbg_register *)arg; - if (cmd == VIDIOC_DBG_S_REGISTER) val = req->val; - ret = pvr2_hdw_register_access( - hdw, &req->match, req->reg, - cmd == VIDIOC_DBG_S_REGISTER, &val); - if (cmd == VIDIOC_DBG_G_REGISTER) req->val = val; - break; - } -#endif - - default : - ret = -ENOTTY; - break; - } - - pvr2_hdw_commit_ctl(hdw); - - if (ret < 0) { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); - } else { - if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl failure, ret=%ld" - " command was:", ret); - v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), - cmd); - } - } - } else { - pvr2_trace(PVR2_TRACE_V4LIOCTL, - "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", - ret, ret); } return ret; } +static int pvr2_s_input(struct file *file, void *priv, unsigned int inp) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (inp >= fh->input_cnt) + return -EINVAL; + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), + fh->input_map[inp]); +} + +static int pvr2_enumaudio(struct file *file, void *priv, struct v4l2_audio *vin) +{ + /* pkt: FIXME: We are returning one "fake" input here + which could very well be called "whatever_we_like". + This is for apps that want to see an audio input + just to feel comfortable, as well as to test if + it can do stereo or sth. There is actually no guarantee + that the actual audio input cannot change behind the app's + back, but most applications should not mind that either. + + Hopefully, mplayer people will work with us on this (this + whole mess is to support mplayer pvr://), or Hans will come + up with a more standard way to say "we have inputs but we + don 't want you to change them independent of video" which + will sort this mess. + */ + + if (vin->index > 0) + return -EINVAL; + strncpy(vin->name, "PVRUSB2 Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int pvr2_g_audio(struct file *file, void *priv, struct v4l2_audio *vin) +{ + /* pkt: FIXME: see above comment (VIDIOC_ENUMAUDIO) */ + vin->index = 0; + strncpy(vin->name, "PVRUSB2 Audio", 14); + vin->capability = V4L2_AUDCAP_STEREO; + return 0; +} + +static int pvr2_s_audio(struct file *file, void *priv, struct v4l2_audio *vout) +{ + if (vout->index) + return -EINVAL; + return 0; +} + +static int pvr2_g_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (vt->index != 0) + return -EINVAL; /* Only answer for the 1st tuner */ + + pvr2_hdw_execute_tuner_poll(hdw); + return pvr2_hdw_get_tuner_status(hdw, vt); +} + +static int pvr2_s_tuner(struct file *file, void *priv, struct v4l2_tuner *vt) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (vt->index != 0) + return -EINVAL; + + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_AUDIOMODE), + vt->audmode); +} + +int pvr2_s_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned long fv; + struct v4l2_tuner vt; + int cur_input; + struct pvr2_ctrl *ctrlp; + int ret; + + ret = pvr2_hdw_get_tuner_status(hdw, &vt); + if (ret != 0) + return ret; + ctrlp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT); + ret = pvr2_ctrl_get_value(ctrlp, &cur_input); + if (ret != 0) + return ret; + if (vf->type == V4L2_TUNER_RADIO) { + if (cur_input != PVR2_CVAL_INPUT_RADIO) + pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_RADIO); + } else { + if (cur_input == PVR2_CVAL_INPUT_RADIO) + pvr2_ctrl_set_value(ctrlp, PVR2_CVAL_INPUT_TV); + } + fv = vf->frequency; + if (vt.capability & V4L2_TUNER_CAP_LOW) + fv = (fv * 125) / 2; + else + fv = fv * 62500; + return pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),fv); +} + +static int pvr2_g_frequency(struct file *file, void *priv, struct v4l2_frequency *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int cur_input; + struct v4l2_tuner vt; + int ret; + + ret = pvr2_hdw_get_tuner_status(hdw, &vt); + if (ret != 0) + return ret; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_FREQUENCY), + &val); + if (ret != 0) + return ret; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_INPUT), + &cur_input); + if (cur_input == PVR2_CVAL_INPUT_RADIO) + vf->type = V4L2_TUNER_RADIO; + else + vf->type = V4L2_TUNER_ANALOG_TV; + if (vt.capability & V4L2_TUNER_CAP_LOW) + val = (val * 2) / 125; + else + val /= 62500; + vf->frequency = val; + return 0; +} + +static int pvr2_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fd) +{ + /* Only one format is supported : mpeg.*/ + if (fd->index != 0) + return -EINVAL; + + memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc)); + return 0; +} + +static int pvr2_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val; + + memcpy(vf, &pvr_format[PVR_FORMAT_PIX], sizeof(struct v4l2_format)); + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES), + &val); + vf->fmt.pix.width = val; + val = 0; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES), + &val); + vf->fmt.pix.height = val; + return 0; +} + +static int pvr2_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int lmin, lmax, ldef; + struct pvr2_ctrl *hcp, *vcp; + int h = vf->fmt.pix.height; + int w = vf->fmt.pix.width; + + hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); + + lmin = pvr2_ctrl_get_min(hcp); + lmax = pvr2_ctrl_get_max(hcp); + pvr2_ctrl_get_def(hcp, &ldef); + if (w == -1) + w = ldef; + else if (w < lmin) + w = lmin; + else if (w > lmax) + w = lmax; + lmin = pvr2_ctrl_get_min(vcp); + lmax = pvr2_ctrl_get_max(vcp); + pvr2_ctrl_get_def(vcp, &ldef); + if (h == -1) + h = ldef; + else if (h < lmin) + h = lmin; + else if (h > lmax) + h = lmax; + + memcpy(vf, &pvr_format[PVR_FORMAT_PIX], + sizeof(struct v4l2_format)); + vf->fmt.pix.width = w; + vf->fmt.pix.height = h; + return 0; +} + +static int pvr2_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *vf) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *hcp, *vcp; + int ret = pvr2_try_fmt_vid_cap(file, fh, vf); + + if (ret) + return ret; + hcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_HRES); + vcp = pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_VRES); + pvr2_ctrl_set_value(hcp, vf->fmt.pix.width); + pvr2_ctrl_set_value(vcp, vf->fmt.pix.height); + return 0; +} + +static int pvr2_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_v4l2_dev *pdi = fh->pdi; + int ret; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + return -EPERM; + } + ret = pvr2_hdw_set_stream_type(hdw, pdi->config); + if (ret < 0) + return ret; + return pvr2_hdw_set_streaming(hdw, !0); +} + +static int pvr2_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + if (!fh->pdi->stream) { + /* No stream defined for this node. This means + that we're not currently allowed to stream from + this node. */ + return -EPERM; + } + return pvr2_hdw_set_streaming(hdw, 0); +} + +static int pvr2_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct pvr2_ctrl *cptr; + int val; + int ret; + + ret = 0; + if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) { + cptr = pvr2_hdw_get_ctrl_nextv4l( + hdw, (vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL)); + if (cptr) + vc->id = pvr2_ctrl_get_v4lid(cptr); + } else { + cptr = pvr2_hdw_get_ctrl_v4l(hdw, vc->id); + } + if (!cptr) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x not implemented here", + vc->id); + return -EINVAL; + } + + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x mapping name=%s (%s)", + vc->id, pvr2_ctrl_get_name(cptr), + pvr2_ctrl_get_desc(cptr)); + strlcpy(vc->name, pvr2_ctrl_get_desc(cptr), sizeof(vc->name)); + vc->flags = pvr2_ctrl_get_v4lflags(cptr); + pvr2_ctrl_get_def(cptr, &val); + vc->default_value = val; + switch (pvr2_ctrl_get_type(cptr)) { + case pvr2_ctl_enum: + vc->type = V4L2_CTRL_TYPE_MENU; + vc->minimum = 0; + vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1; + vc->step = 1; + break; + case pvr2_ctl_bool: + vc->type = V4L2_CTRL_TYPE_BOOLEAN; + vc->minimum = 0; + vc->maximum = 1; + vc->step = 1; + break; + case pvr2_ctl_int: + vc->type = V4L2_CTRL_TYPE_INTEGER; + vc->minimum = pvr2_ctrl_get_min(cptr); + vc->maximum = pvr2_ctrl_get_max(cptr); + vc->step = 1; + break; + default: + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "QUERYCTRL id=0x%x name=%s not mappable", + vc->id, pvr2_ctrl_get_name(cptr)); + return -EINVAL; + } + return 0; +} + +static int pvr2_querymenu(struct file *file, void *priv, struct v4l2_querymenu *vm) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + unsigned int cnt = 0; + int ret; + + ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw, vm->id), + vm->index, + vm->name, sizeof(vm->name) - 1, + &cnt); + vm->name[cnt] = 0; + return ret; +} + +static int pvr2_g_ctrl(struct file *file, void *priv, struct v4l2_control *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + &val); + vc->value = val; + return ret; +} + +static int pvr2_s_ctrl(struct file *file, void *priv, struct v4l2_control *vc) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + return pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw, vc->id), + vc->value); +} + +static int pvr2_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + unsigned int idx; + int val; + int ret; + + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), &val); + if (ret) { + ctls->error_idx = idx; + return ret; + } + /* Ensure that if read as a 64 bit value, the user + will still get a hopefully sane value */ + ctrl->value64 = 0; + ctrl->value = val; + } + return 0; +} + +static int pvr2_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + unsigned int idx; + int ret; + + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id), + ctrl->value); + if (ret) { + ctls->error_idx = idx; + return ret; + } + } + return 0; +} + +static int pvr2_try_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctls) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_ext_control *ctrl; + struct pvr2_ctrl *pctl; + unsigned int idx; + int ret; + + /* For the moment just validate that the requested control + actually exists. */ + ret = 0; + for (idx = 0; idx < ctls->count; idx++) { + ctrl = ctls->controls + idx; + pctl = pvr2_hdw_get_ctrl_v4l(hdw, ctrl->id); + if (!pctl) { + ctls->error_idx = idx; + return -EINVAL; + } + } + return 0; +} + +static int pvr2_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int ret; + + if (cap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_hdw_get_cropcap(hdw, cap); + cap->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* paranoia */ + return ret; +} + +static int pvr2_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + int val = 0; + int ret; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), &val); + if (ret != 0) + return -EINVAL; + crop->c.left = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), &val); + if (ret != 0) + return -EINVAL; + crop->c.top = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), &val); + if (ret != 0) + return -EINVAL; + crop->c.width = val; + ret = pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), &val); + if (ret != 0) + return -EINVAL; + crop->c.height = val; + return 0; +} + +static int pvr2_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + struct v4l2_cropcap cap; + int ret; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPL), + crop->c.left); + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPT), + crop->c.top); + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPW), + crop->c.width); + if (ret != 0) + return -EINVAL; + ret = pvr2_ctrl_set_value( + pvr2_hdw_get_ctrl_by_id(hdw, PVR2_CID_CROPH), + crop->c.height); + if (ret != 0) + return -EINVAL; + return 0; +} + +static int pvr2_log_status(struct file *file, void *priv) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + + pvr2_hdw_trigger_module_log(hdw); + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int pvr2_g_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + u64 val; + int ret; + + ret = pvr2_hdw_register_access( + hdw, &req->match, req->reg, + 0, &val); + req->val = val; + return ret; +} + +static int pvr2_s_register(struct file *file, void *priv, struct v4l2_dbg_register *req) +{ + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + u64 val; + int ret; + + val = req->val; + ret = pvr2_hdw_register_access( + hdw, &req->match, req->reg, + 1, &val); + return ret; +} +#endif + +static const struct v4l2_ioctl_ops pvr2_ioctl_ops = { + .vidioc_querycap = pvr2_querycap, + .vidioc_g_priority = pvr2_g_priority, + .vidioc_s_priority = pvr2_s_priority, + .vidioc_s_audio = pvr2_s_audio, + .vidioc_g_audio = pvr2_g_audio, + .vidioc_enumaudio = pvr2_enumaudio, + .vidioc_enum_input = pvr2_enum_input, + .vidioc_cropcap = pvr2_cropcap, + .vidioc_s_crop = pvr2_s_crop, + .vidioc_g_crop = pvr2_g_crop, + .vidioc_g_input = pvr2_g_input, + .vidioc_s_input = pvr2_s_input, + .vidioc_g_frequency = pvr2_g_frequency, + .vidioc_s_frequency = pvr2_s_frequency, + .vidioc_s_tuner = pvr2_s_tuner, + .vidioc_g_tuner = pvr2_g_tuner, + .vidioc_g_std = pvr2_g_std, + .vidioc_s_std = pvr2_s_std, + .vidioc_querystd = pvr2_querystd, + .vidioc_log_status = pvr2_log_status, + .vidioc_enum_fmt_vid_cap = pvr2_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = pvr2_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = pvr2_s_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = pvr2_try_fmt_vid_cap, + .vidioc_streamon = pvr2_streamon, + .vidioc_streamoff = pvr2_streamoff, + .vidioc_queryctrl = pvr2_queryctrl, + .vidioc_querymenu = pvr2_querymenu, + .vidioc_g_ctrl = pvr2_g_ctrl, + .vidioc_s_ctrl = pvr2_s_ctrl, + .vidioc_g_ext_ctrls = pvr2_g_ext_ctrls, + .vidioc_s_ext_ctrls = pvr2_s_ext_ctrls, + .vidioc_try_ext_ctrls = pvr2_try_ext_ctrls, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = pvr2_g_register, + .vidioc_s_register = pvr2_s_register, +#endif +}; + static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip) { struct pvr2_hdw *hdw = dip->v4lp->channel.mc_head->hdw; @@ -961,7 +959,56 @@ static long pvr2_v4l2_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl); + struct pvr2_v4l2_fh *fh = file->private_data; + struct pvr2_v4l2 *vp = fh->vhead; + struct pvr2_hdw *hdw = fh->channel.mc_head->hdw; + long ret = -EINVAL; + + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) + v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), cmd); + + if (!pvr2_hdw_dev_ok(hdw)) { + pvr2_trace(PVR2_TRACE_ERROR_LEGS, + "ioctl failed - bad or no context"); + return -EFAULT; + } + + /* check priority */ + switch (cmd) { + case VIDIOC_S_CTRL: + case VIDIOC_S_STD: + case VIDIOC_S_INPUT: + case VIDIOC_S_TUNER: + case VIDIOC_S_FREQUENCY: + ret = v4l2_prio_check(&vp->prio, fh->prio); + if (ret) + return ret; + } + + ret = video_ioctl2(file, cmd, arg); + + pvr2_hdw_commit_ctl(hdw); + + if (ret < 0) { + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl failure, ret=%ld", ret); + } else { + if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl failure, ret=%ld" + " command was:", ret); + v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw), + cmd); + } + } + } else { + pvr2_trace(PVR2_TRACE_V4LIOCTL, + "pvr2_v4l2_do_ioctl complete, ret=%ld (0x%lx)", + ret, ret); + } + return ret; + } @@ -1262,10 +1309,12 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, struct usb_device *usbdev; int mindevnum; int unit_number; + struct pvr2_hdw *hdw; int *nr_ptr = NULL; dip->v4lp = vp; - usbdev = pvr2_hdw_get_dev(vp->channel.mc_head->hdw); + hdw = vp->channel.mc_head->hdw; + usbdev = pvr2_hdw_get_dev(hdw); dip->v4l_type = v4l_type; switch (v4l_type) { case VFL_TYPE_GRABBER: @@ -1300,9 +1349,17 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, memcpy(&dip->devbase,&vdev_template,sizeof(vdev_template)); dip->devbase.release = pvr2_video_device_release; + dip->devbase.ioctl_ops = &pvr2_ioctl_ops; + { + int val; + pvr2_ctrl_get_value( + pvr2_hdw_get_ctrl_by_id(hdw, + PVR2_CID_STDAVAIL), &val); + dip->devbase.tvnorms = (v4l2_std_id)val; + } mindevnum = -1; - unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw); + unit_number = pvr2_hdw_get_unit_number(hdw); if (nr_ptr && (unit_number >= 0) && (unit_number < PVR_NUM)) { mindevnum = nr_ptr[unit_number]; } @@ -1319,7 +1376,7 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip, video_device_node_name(&dip->devbase), pvr2_config_get_name(dip->config)); - pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw, + pvr2_hdw_v4l_store_minor_number(hdw, dip->minor_type,dip->devbase.minor); } diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c index 122fbd0081eb..ec4e2ef54e65 100644 --- a/drivers/media/video/pwc/pwc-if.c +++ b/drivers/media/video/pwc/pwc-if.c @@ -357,6 +357,7 @@ handler_end: PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i); } +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ static int pwc_isoc_init(struct pwc_device *pdev) { struct usb_device *udev; @@ -366,9 +367,6 @@ static int pwc_isoc_init(struct pwc_device *pdev) struct usb_host_interface *idesc = NULL; int compression = 0; /* 0..3 = uncompressed..high */ - if (pdev->iso_init) - return 0; - pdev->vsync = 0; pdev->vlast_packet_size = 0; pdev->fill_buf = NULL; @@ -418,7 +416,6 @@ retry: urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { PWC_ERROR("Failed to allocate urb %d\n", i); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return -ENOMEM; } @@ -435,7 +432,6 @@ retry: &urb->transfer_dma); if (urb->transfer_buffer == NULL) { PWC_ERROR("Failed to allocate urb buffer %d\n", i); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return -ENOMEM; } @@ -455,13 +451,11 @@ retry: ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL); if (ret == -ENOSPC && compression < 3) { compression++; - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); goto retry; } if (ret) { PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); - pdev->iso_init = 1; pwc_isoc_cleanup(pdev); return ret; } @@ -469,7 +463,6 @@ retry: } /* All is done... */ - pdev->iso_init = 1; PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); return 0; } @@ -507,21 +500,19 @@ static void pwc_iso_free(struct pwc_device *pdev) } } +/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ static void pwc_isoc_cleanup(struct pwc_device *pdev) { PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); - if (pdev->iso_init == 0) - return; - pwc_iso_stop(pdev); pwc_iso_free(pdev); usb_set_interface(pdev->udev, 0, 0); - pdev->iso_init = 0; PWC_DEBUG_OPEN("<< pwc_isoc_cleanup()\n"); } +/* Must be called with vb_queue_lock hold */ static void pwc_cleanup_queued_bufs(struct pwc_device *pdev) { unsigned long flags = 0; @@ -573,18 +564,13 @@ static const char *pwc_sensor_type_to_string(unsigned int sensor_type) int pwc_test_n_set_capt_file(struct pwc_device *pdev, struct file *file) { - int r = 0; - - mutex_lock(&pdev->capt_file_lock); if (pdev->capt_file != NULL && - pdev->capt_file != file) { - r = -EBUSY; - goto leave; - } + pdev->capt_file != file) + return -EBUSY; + pdev->capt_file = file; -leave: - mutex_unlock(&pdev->capt_file_lock); - return r; + + return 0; } static void pwc_video_release(struct v4l2_device *v) @@ -592,6 +578,7 @@ static void pwc_video_release(struct v4l2_device *v) struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); v4l2_ctrl_handler_free(&pdev->ctrl_handler); + v4l2_device_unregister(&pdev->v4l2_dev); kfree(pdev->ctrl_buf); kfree(pdev); } @@ -600,10 +587,25 @@ static int pwc_video_close(struct file *file) { struct pwc_device *pdev = video_drvdata(file); + /* + * If we're still streaming vb2_queue_release will call stream_stop + * so we must take both the v4l2_lock and the vb_queue_lock. + */ + if (mutex_lock_interruptible(&pdev->v4l2_lock)) + return -ERESTARTSYS; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) { + mutex_unlock(&pdev->v4l2_lock); + return -ERESTARTSYS; + } + if (pdev->capt_file == file) { vb2_queue_release(&pdev->vb_queue); pdev->capt_file = NULL; } + + mutex_unlock(&pdev->vb_queue_lock); + mutex_unlock(&pdev->v4l2_lock); + return v4l2_fh_release(file); } @@ -611,35 +613,81 @@ static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct pwc_device *pdev = video_drvdata(file); + int lock_v4l2 = 0; + ssize_t ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) + goto out; - return vb2_read(&pdev->vb_queue, buf, count, ppos, - file->f_flags & O_NONBLOCK); + /* stream_start will get called so we must take the v4l2_lock */ + if (pdev->vb_queue.fileio == NULL) + lock_v4l2 = 1; + + /* Use try_lock, since we're taking the locks in the *wrong* order! */ + if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) { + ret = -ERESTARTSYS; + goto out; + } + ret = vb2_read(&pdev->vb_queue, buf, count, ppos, + file->f_flags & O_NONBLOCK); + if (lock_v4l2) + mutex_unlock(&pdev->v4l2_lock); +out: + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static unsigned int pwc_video_poll(struct file *file, poll_table *wait) { struct pwc_device *pdev = video_drvdata(file); + struct vb2_queue *q = &pdev->vb_queue; + unsigned long req_events = poll_requested_events(wait); + unsigned int ret = POLL_ERR; + int lock_v4l2 = 0; - if (!pdev->udev) + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) return POLL_ERR; - return vb2_poll(&pdev->vb_queue, file, wait); + /* Will this start fileio and thus call start_stream? */ + if ((req_events & (POLLIN | POLLRDNORM)) && + q->num_buffers == 0 && !q->streaming && q->fileio == NULL) { + if (pwc_test_n_set_capt_file(pdev, file)) + goto out; + lock_v4l2 = 1; + } + + /* Use try_lock, since we're taking the locks in the *wrong* order! */ + if (lock_v4l2 && !mutex_trylock(&pdev->v4l2_lock)) + goto out; + ret = vb2_poll(&pdev->vb_queue, file, wait); + if (lock_v4l2) + mutex_unlock(&pdev->v4l2_lock); + +out: + if (!pdev->udev) + ret |= POLLHUP; + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (pdev->capt_file != file) - return -EBUSY; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - return vb2_mmap(&pdev->vb_queue, vma); + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_mmap(&pdev->vb_queue, vma); + + mutex_unlock(&pdev->vb_queue_lock); + return ret; } /***************************************************************************/ @@ -715,12 +763,14 @@ static void buffer_queue(struct vb2_buffer *vb) struct pwc_frame_buf *buf = container_of(vb, struct pwc_frame_buf, vb); unsigned long flags = 0; - spin_lock_irqsave(&pdev->queued_bufs_lock, flags); /* Check the device has not disconnected between prep and queuing */ - if (pdev->udev) - list_add_tail(&buf->list, &pdev->queued_bufs); - else + if (!pdev->udev) { vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + return; + } + + spin_lock_irqsave(&pdev->queued_bufs_lock, flags); + list_add_tail(&buf->list, &pdev->queued_bufs); spin_unlock_irqrestore(&pdev->queued_bufs_lock, flags); } @@ -729,11 +779,8 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) struct pwc_device *pdev = vb2_get_drv_priv(vq); int r; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - r = -ENODEV; - goto leave; - } + if (!pdev->udev) + return -ENODEV; /* Turn on camera and set LEDS on */ pwc_camera_power(pdev, 1); @@ -747,8 +794,7 @@ static int start_streaming(struct vb2_queue *vq, unsigned int count) /* And cleanup any queued bufs!! */ pwc_cleanup_queued_bufs(pdev); } -leave: - mutex_unlock(&pdev->udevlock); + return r; } @@ -756,19 +802,29 @@ static int stop_streaming(struct vb2_queue *vq) { struct pwc_device *pdev = vb2_get_drv_priv(vq); - mutex_lock(&pdev->udevlock); if (pdev->udev) { pwc_set_leds(pdev, 0, 0); pwc_camera_power(pdev, 0); pwc_isoc_cleanup(pdev); } - mutex_unlock(&pdev->udevlock); pwc_cleanup_queued_bufs(pdev); return 0; } +static void wait_prepare(struct vb2_queue *vq) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + mutex_unlock(&pdev->vb_queue_lock); +} + +static void wait_finish(struct vb2_queue *vq) +{ + struct pwc_device *pdev = vb2_get_drv_priv(vq); + mutex_lock(&pdev->vb_queue_lock); +} + static struct vb2_ops pwc_vb_queue_ops = { .queue_setup = queue_setup, .buf_init = buffer_init, @@ -778,6 +834,8 @@ static struct vb2_ops pwc_vb_queue_ops = { .buf_queue = buffer_queue, .start_streaming = start_streaming, .stop_streaming = stop_streaming, + .wait_prepare = wait_prepare, + .wait_finish = wait_finish, }; /***************************************************************************/ @@ -1057,8 +1115,8 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->features = features; pwc_construct(pdev); /* set min/max sizes correct */ - mutex_init(&pdev->capt_file_lock); - mutex_init(&pdev->udevlock); + mutex_init(&pdev->v4l2_lock); + mutex_init(&pdev->vb_queue_lock); spin_lock_init(&pdev->queued_bufs_lock); INIT_LIST_HEAD(&pdev->queued_bufs); @@ -1130,6 +1188,16 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id pdev->v4l2_dev.ctrl_handler = &pdev->ctrl_handler; pdev->vdev.v4l2_dev = &pdev->v4l2_dev; + pdev->vdev.lock = &pdev->v4l2_lock; + + /* + * Don't take v4l2_lock for these ioctls. This improves latency if + * v4l2_lock is taken for a long time, e.g. when changing a control + * value, and a new frame is ready to be dequeued. + */ + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_DQBUF); + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QBUF); + v4l2_disable_ioctl_locking(&pdev->vdev, VIDIOC_QUERYBUF); rc = video_register_device(&pdev->vdev, VFL_TYPE_GRABBER, -1); if (rc < 0) { @@ -1185,16 +1253,20 @@ static void usb_pwc_disconnect(struct usb_interface *intf) struct v4l2_device *v = usb_get_intfdata(intf); struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev); - mutex_lock(&pdev->udevlock); + mutex_lock(&pdev->v4l2_lock); + + mutex_lock(&pdev->vb_queue_lock); /* No need to keep the urbs around after disconnection */ - pwc_isoc_cleanup(pdev); + if (pdev->vb_queue.streaming) + pwc_isoc_cleanup(pdev); pdev->udev = NULL; - mutex_unlock(&pdev->udevlock); - pwc_cleanup_queued_bufs(pdev); + mutex_unlock(&pdev->vb_queue_lock); + v4l2_device_disconnect(&pdev->v4l2_dev); video_unregister_device(&pdev->vdev); - v4l2_device_unregister(&pdev->v4l2_dev); + + mutex_unlock(&pdev->v4l2_lock); #ifdef CONFIG_USB_PWC_INPUT_EVDEV if (pdev->button_dev) @@ -1229,15 +1301,4 @@ MODULE_LICENSE("GPL"); MODULE_ALIAS("pwcx"); MODULE_VERSION( PWC_VERSION ); -static int __init usb_pwc_init(void) -{ - return usb_register(&pwc_driver); -} - -static void __exit usb_pwc_exit(void) -{ - usb_deregister(&pwc_driver); -} - -module_init(usb_pwc_init); -module_exit(usb_pwc_exit); +module_usb_driver(pwc_driver); diff --git a/drivers/media/video/pwc/pwc-v4l.c b/drivers/media/video/pwc/pwc-v4l.c index 2834e3e65b39..c691e29cc36e 100644 --- a/drivers/media/video/pwc/pwc-v4l.c +++ b/drivers/media/video/pwc/pwc-v4l.c @@ -464,26 +464,24 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) struct pwc_device *pdev = video_drvdata(file); int ret, pixelformat, compression = 0; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; - ret = pwc_vidioc_try_fmt(pdev, f); if (ret < 0) return ret; - pixelformat = f->fmt.pix.pixelformat; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - ret = -ENODEV; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) goto leave; - } - if (pdev->iso_init) { + if (pdev->vb_queue.streaming) { ret = -EBUSY; goto leave; } + pixelformat = f->fmt.pix.pixelformat; + PWC_DEBUG_IOCTL("Trying to set format to: width=%d height=%d fps=%d " "format=%c%c%c%c\n", f->fmt.pix.width, f->fmt.pix.height, pdev->vframes, @@ -499,7 +497,7 @@ static int pwc_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); leave: - mutex_unlock(&pdev->udevlock); + mutex_unlock(&pdev->vb_queue_lock); return ret; } @@ -507,9 +505,6 @@ static int pwc_querycap(struct file *file, void *fh, struct v4l2_capability *cap { struct pwc_device *pdev = video_drvdata(file); - if (!pdev->udev) - return -ENODEV; - strcpy(cap->driver, PWC_NAME); strlcpy(cap->card, pdev->vdev.name, sizeof(cap->card)); usb_make_path(pdev->udev, cap->bus_info, sizeof(cap->bus_info)); @@ -540,15 +535,12 @@ static int pwc_s_input(struct file *file, void *fh, unsigned int i) return i ? -EINVAL : 0; } -static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl) +static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) { struct pwc_device *pdev = container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - if (!pdev->udev) - return -ENODEV; - switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: if (pdev->color_bal_valid && @@ -615,18 +607,6 @@ static int pwc_g_volatile_ctrl_unlocked(struct v4l2_ctrl *ctrl) return ret; } -static int pwc_g_volatile_ctrl(struct v4l2_ctrl *ctrl) -{ - struct pwc_device *pdev = - container_of(ctrl->handler, struct pwc_device, ctrl_handler); - int ret; - - mutex_lock(&pdev->udevlock); - ret = pwc_g_volatile_ctrl_unlocked(ctrl); - mutex_unlock(&pdev->udevlock); - return ret; -} - static int pwc_set_awb(struct pwc_device *pdev) { int ret; @@ -648,7 +628,7 @@ static int pwc_set_awb(struct pwc_device *pdev) if (pdev->auto_white_balance->val == awb_indoor || pdev->auto_white_balance->val == awb_outdoor || pdev->auto_white_balance->val == awb_fl) - pwc_g_volatile_ctrl_unlocked(pdev->auto_white_balance); + pwc_g_volatile_ctrl(pdev->auto_white_balance); } if (pdev->auto_white_balance->val != awb_manual) return 0; @@ -812,13 +792,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) container_of(ctrl->handler, struct pwc_device, ctrl_handler); int ret = 0; - mutex_lock(&pdev->udevlock); - - if (!pdev->udev) { - ret = -ENODEV; - goto leave; - } - switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: ret = pwc_set_u8_ctrl(pdev, SET_LUM_CTL, @@ -915,8 +888,6 @@ static int pwc_s_ctrl(struct v4l2_ctrl *ctrl) if (ret) PWC_ERROR("s_ctrl %s error %d\n", ctrl->name, ret); -leave: - mutex_unlock(&pdev->udevlock); return ret; } @@ -949,11 +920,9 @@ static int pwc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f) if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; - mutex_lock(&pdev->udevlock); /* To avoid race with s_fmt */ PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n", pdev->width, pdev->height); pwc_vidioc_fill_fmt(f, pdev->width, pdev->height, pdev->pixfmt); - mutex_unlock(&pdev->udevlock); return 0; } @@ -968,70 +937,98 @@ static int pwc_reqbufs(struct file *file, void *fh, struct v4l2_requestbuffers *rb) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - return vb2_reqbufs(&pdev->vb_queue, rb); + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_reqbufs(&pdev->vb_queue, rb); + + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - return vb2_querybuf(&pdev->vb_queue, buf); + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; + + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_querybuf(&pdev->vb_queue, buf); + + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_qbuf(&pdev->vb_queue, buf); - return vb2_qbuf(&pdev->vb_queue, buf); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_dqbuf(&pdev->vb_queue, buf, + file->f_flags & O_NONBLOCK); - return vb2_dqbuf(&pdev->vb_queue, buf, file->f_flags & O_NONBLOCK); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_streamon(struct file *file, void *fh, enum v4l2_buf_type i) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_streamon(&pdev->vb_queue, i); - return vb2_streamon(&pdev->vb_queue, i); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_streamoff(struct file *file, void *fh, enum v4l2_buf_type i) { struct pwc_device *pdev = video_drvdata(file); + int ret; - if (!pdev->udev) - return -ENODEV; + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->capt_file != file) - return -EBUSY; + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret == 0) + ret = vb2_streamoff(&pdev->vb_queue, i); - return vb2_streamoff(&pdev->vb_queue, i); + mutex_unlock(&pdev->vb_queue_lock); + return ret; } static int pwc_enum_framesizes(struct file *file, void *fh, @@ -1119,19 +1116,17 @@ static int pwc_s_parm(struct file *file, void *fh, parm->parm.capture.timeperframe.numerator == 0) return -EINVAL; - if (pwc_test_n_set_capt_file(pdev, file)) - return -EBUSY; - fps = parm->parm.capture.timeperframe.denominator / parm->parm.capture.timeperframe.numerator; - mutex_lock(&pdev->udevlock); - if (!pdev->udev) { - ret = -ENODEV; - goto leave; - } + if (mutex_lock_interruptible(&pdev->vb_queue_lock)) + return -ERESTARTSYS; - if (pdev->iso_init) { + ret = pwc_test_n_set_capt_file(pdev, file); + if (ret) + goto leave; + + if (pdev->vb_queue.streaming) { ret = -EBUSY; goto leave; } @@ -1142,7 +1137,7 @@ static int pwc_s_parm(struct file *file, void *fh, pwc_g_parm(file, fh, parm); leave: - mutex_unlock(&pdev->udevlock); + mutex_unlock(&pdev->vb_queue_lock); return ret; } @@ -1166,4 +1161,6 @@ const struct v4l2_ioctl_ops pwc_ioctl_ops = { .vidioc_enum_frameintervals = pwc_enum_frameintervals, .vidioc_g_parm = pwc_g_parm, .vidioc_s_parm = pwc_s_parm, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h index e4d4d711dd1f..d6b5b216b9d6 100644 --- a/drivers/media/video/pwc/pwc.h +++ b/drivers/media/video/pwc/pwc.h @@ -221,9 +221,17 @@ struct pwc_device struct video_device vdev; struct v4l2_device v4l2_dev; - /* Pointer to our usb_device, may be NULL after unplug */ - struct usb_device *udev; - struct mutex udevlock; + /* videobuf2 queue and queued buffers list */ + struct vb2_queue vb_queue; + struct list_head queued_bufs; + spinlock_t queued_bufs_lock; /* Protects queued_bufs */ + + /* Note if taking both locks v4l2_lock must always be locked first! */ + struct mutex v4l2_lock; /* Protects everything else */ + struct mutex vb_queue_lock; /* Protects vb_queue and capt_file */ + + /* Pointer to our usb_device, will be NULL after unplug */ + struct usb_device *udev; /* Both mutexes most be hold when setting! */ /* type of cam (645, 646, 675, 680, 690, 720, 730, 740, 750) */ int type; @@ -232,7 +240,6 @@ struct pwc_device /*** Video data ***/ struct file *capt_file; /* file doing video capture */ - struct mutex capt_file_lock; int vendpoint; /* video isoc endpoint */ int vcinterface; /* video control interface */ int valternate; /* alternate interface needed */ @@ -251,12 +258,6 @@ struct pwc_device unsigned char *ctrl_buf; struct urb *urbs[MAX_ISO_BUFS]; - char iso_init; - - /* videobuf2 queue and queued buffers list */ - struct vb2_queue vb_queue; - struct list_head queued_bufs; - spinlock_t queued_bufs_lock; /* * Frame currently being filled, this only gets touched by the diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c index 5a413f4427e0..9c21e01f2c24 100644 --- a/drivers/media/video/pxa_camera.c +++ b/drivers/media/video/pxa_camera.c @@ -241,15 +241,10 @@ static int pxa_videobuf_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct soc_camera_device *icd = vq->priv_data; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; dev_dbg(icd->parent, "count=%d, size=%d\n", *count, *size); - *size = bytes_per_line * icd->user_height; + *size = icd->sizeimage; if (0 == *count) *count = 32; @@ -435,11 +430,6 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, struct pxa_buffer *buf = container_of(vb, struct pxa_buffer, vb); int ret; int size_y, size_u = 0, size_v = 0; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - - if (bytes_per_line < 0) - return bytes_per_line; dev_dbg(dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__, vb, vb->baddr, vb->bsize); @@ -474,7 +464,7 @@ static int pxa_videobuf_prepare(struct videobuf_queue *vq, vb->state = VIDEOBUF_NEEDS_INIT; } - vb->size = bytes_per_line * vb->height; + vb->size = icd->sizeimage; if (0 != vb->baddr && vb->bsize < vb->size) { ret = -EINVAL; goto out; @@ -1244,6 +1234,7 @@ static const struct soc_mbus_pixelfmt pxa_camera_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, }, }; diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c index 4894cbb1c547..01c2179f0520 100644 --- a/drivers/media/video/s2255drv.c +++ b/drivers/media/video/s2255drv.c @@ -634,13 +634,11 @@ static void s2255_fillbuff(struct s2255_channel *channel, const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; - struct s2255_framei *frm; if (!vbuf) return; last_frame = channel->last_frame; if (last_frame != -1) { - frm = &channel->buffer.frame[last_frame]; tmpbuf = (const char *)channel->buffer.frame[last_frame].lpvbits; switch (buf->fmt->fourcc) { @@ -987,7 +985,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct videobuf_queue *q = &fh->vb_vidq; struct s2255_mode mode; int ret; - int norm; ret = vidioc_try_fmt_vid_cap(file, fh, f); @@ -1018,7 +1015,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, channel->height = f->fmt.pix.height; fh->vb_vidq.field = f->fmt.pix.field; fh->type = f->type; - norm = norm_minw(&channel->vdev); if (channel->width > norm_minw(&channel->vdev)) { if (channel->height > norm_minh(&channel->vdev)) { if (channel->cap_parm.capturemode & @@ -1826,8 +1822,7 @@ static void s2255_destroy(struct s2255_dev *dev) usb_free_urb(dev->fw_data->fw_urb); dev->fw_data->fw_urb = NULL; } - if (dev->fw_data->fw) - release_firmware(dev->fw_data->fw); + release_firmware(dev->fw_data->fw); kfree(dev->fw_data->pfw_data); kfree(dev->fw_data); /* reset the DSP so firmware can be reloaded next time */ @@ -1949,6 +1944,10 @@ static int s2255_probe_v4l(struct s2255_dev *dev) /* register 4 video devices */ channel->vdev = template; channel->vdev.lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &channel->vdev.flags); channel->vdev.v4l2_dev = &dev->v4l2_dev; video_set_drvdata(&channel->vdev, channel); if (video_nr == -1) diff --git a/drivers/media/video/s5p-fimc/Kconfig b/drivers/media/video/s5p-fimc/Kconfig new file mode 100644 index 000000000000..a564f7eeb064 --- /dev/null +++ b/drivers/media/video/s5p-fimc/Kconfig @@ -0,0 +1,48 @@ + +config VIDEO_SAMSUNG_S5P_FIMC + bool "Samsung S5P/EXYNOS SoC camera interface driver (experimental)" + depends on VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API && PLAT_S5P && PM_RUNTIME + depends on EXPERIMENTAL + help + Say Y here to enable camera host interface devices for + Samsung S5P and EXYNOS SoC series. + +if VIDEO_SAMSUNG_S5P_FIMC + +config VIDEO_S5P_FIMC + tristate "S5P/EXYNOS4 FIMC/CAMIF camera interface driver" + depends on I2C + select VIDEOBUF2_DMA_CONTIG + select V4L2_MEM2MEM_DEV + help + This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC camera host + interface and video postprocessor (FIMC and FIMC-LITE) devices. + + To compile this driver as a module, choose M here: the + module will be called s5p-fimc. + +config VIDEO_S5P_MIPI_CSIS + tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver" + depends on REGULATOR + help + This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2 + receiver (MIPI-CSIS) devices. + + To compile this driver as a module, choose M here: the + module will be called s5p-csis. + +if ARCH_EXYNOS + +config VIDEO_EXYNOS_FIMC_LITE + tristate "EXYNOS FIMC-LITE camera interface driver" + depends on I2C + select VIDEOBUF2_DMA_CONTIG + help + This is a V4L2 driver for Samsung EXYNOS4/5 SoC FIMC-LITE camera + host interface. + + To compile this driver as a module, choose M here: the + module will be called exynos-fimc-lite. +endif + +endif # VIDEO_SAMSUNG_S5P_FIMC diff --git a/drivers/media/video/s5p-fimc/Makefile b/drivers/media/video/s5p-fimc/Makefile index 33dec7f890e7..46485143e1ca 100644 --- a/drivers/media/video/s5p-fimc/Makefile +++ b/drivers/media/video/s5p-fimc/Makefile @@ -1,5 +1,7 @@ -s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-capture.o fimc-mdevice.o +s5p-fimc-objs := fimc-core.o fimc-reg.o fimc-m2m.o fimc-capture.o fimc-mdevice.o +exynos-fimc-lite-objs += fimc-lite-reg.o fimc-lite.o s5p-csis-objs := mipi-csis.o obj-$(CONFIG_VIDEO_S5P_MIPI_CSIS) += s5p-csis.o -obj-$(CONFIG_VIDEO_SAMSUNG_S5P_FIMC) += s5p-fimc.o +obj-$(CONFIG_VIDEO_EXYNOS_FIMC_LITE) += exynos-fimc-lite.o +obj-$(CONFIG_VIDEO_S5P_FIMC) += s5p-fimc.o diff --git a/drivers/media/video/s5p-fimc/fimc-capture.c b/drivers/media/video/s5p-fimc/fimc-capture.c index 7e9b2c612b03..354574591908 100644 --- a/drivers/media/video/s5p-fimc/fimc-capture.c +++ b/drivers/media/video/s5p-fimc/fimc-capture.c @@ -1,8 +1,8 @@ /* * Samsung S5P/EXYNOS4 SoC series camera interface (camera capture) driver * - * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. - * Author: Sylwester Nawrocki, + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * 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 @@ -29,20 +29,22 @@ #include "fimc-mdevice.h" #include "fimc-core.h" +#include "fimc-reg.h" -static int fimc_init_capture(struct fimc_dev *fimc) +static int fimc_capture_hw_init(struct fimc_dev *fimc) { struct fimc_ctx *ctx = fimc->vid_cap.ctx; + struct fimc_pipeline *p = &fimc->pipeline; struct fimc_sensor_info *sensor; unsigned long flags; int ret = 0; - if (fimc->pipeline.sensor == NULL || ctx == NULL) + if (p->subdevs[IDX_SENSOR] == NULL || ctx == NULL) return -ENXIO; if (ctx->s_frame.fmt == NULL) return -EINVAL; - sensor = v4l2_get_subdev_hostdata(fimc->pipeline.sensor); + sensor = v4l2_get_subdev_hostdata(p->subdevs[IDX_SENSOR]); spin_lock_irqsave(&fimc->slock, flags); fimc_prepare_dma_offset(ctx, &ctx->d_frame); @@ -60,7 +62,7 @@ static int fimc_init_capture(struct fimc_dev *fimc) fimc_hw_set_mainscaler(ctx); fimc_hw_set_target_format(ctx); fimc_hw_set_rotation(ctx); - fimc_hw_set_effect(ctx, false); + fimc_hw_set_effect(ctx); fimc_hw_set_output_path(ctx); fimc_hw_set_out_dma(ctx); if (fimc->variant->has_alpha) @@ -71,6 +73,14 @@ static int fimc_init_capture(struct fimc_dev *fimc) return ret; } +/* + * Reinitialize the driver so it is ready to start the streaming again. + * Set fimc->state to indicate stream off and the hardware shut down state. + * If not suspending (@suspend is false), return any buffers to videobuf2. + * Otherwise put any owned buffers onto the pending buffers queue, so they + * can be re-spun when the device is being resumed. Also perform FIMC + * software reset and disable streaming on the whole pipeline if required. + */ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) { struct fimc_vid_cap *cap = &fimc->vid_cap; @@ -83,7 +93,9 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) fimc->state &= ~(1 << ST_CAPT_RUN | 1 << ST_CAPT_SHUT | 1 << ST_CAPT_STREAM | 1 << ST_CAPT_ISP_STREAM); - if (!suspend) + if (suspend) + fimc->state |= (1 << ST_CAPT_SUSPENDED); + else fimc->state &= ~(1 << ST_CAPT_PEND | 1 << ST_CAPT_SUSPENDED); /* Release unused buffers */ @@ -99,7 +111,6 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) else vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); } - set_bit(ST_CAPT_SUSPENDED, &fimc->state); fimc_hw_reset(fimc); cap->buf_index = 0; @@ -107,7 +118,7 @@ static int fimc_capture_state_cleanup(struct fimc_dev *fimc, bool suspend) spin_unlock_irqrestore(&fimc->slock, flags); if (streaming) - return fimc_pipeline_s_stream(fimc, 0); + return fimc_pipeline_s_stream(&fimc->pipeline, 0); else return 0; } @@ -138,32 +149,96 @@ static int fimc_stop_capture(struct fimc_dev *fimc, bool suspend) * spinlock held. It updates the camera pixel crop, rotation and * image flip in H/W. */ -int fimc_capture_config_update(struct fimc_ctx *ctx) +static int fimc_capture_config_update(struct fimc_ctx *ctx) { struct fimc_dev *fimc = ctx->fimc_dev; int ret; - if (!test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) - return 0; - - spin_lock(&ctx->slock); fimc_hw_set_camera_offset(fimc, &ctx->s_frame); + ret = fimc_set_scaler_info(ctx); - if (ret == 0) { - fimc_hw_set_prescaler(ctx); - fimc_hw_set_mainscaler(ctx); - fimc_hw_set_target_format(ctx); - fimc_hw_set_rotation(ctx); - fimc_prepare_dma_offset(ctx, &ctx->d_frame); - fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) - fimc_hw_set_rgb_alpha(ctx); - clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); - } - spin_unlock(&ctx->slock); + if (ret) + return ret; + + fimc_hw_set_prescaler(ctx); + fimc_hw_set_mainscaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_hw_set_effect(ctx); + fimc_prepare_dma_offset(ctx, &ctx->d_frame); + fimc_hw_set_out_dma(ctx); + if (fimc->variant->has_alpha) + fimc_hw_set_rgb_alpha(ctx); + + clear_bit(ST_CAPT_APPLY_CFG, &fimc->state); return ret; } +void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf) +{ + struct fimc_vid_cap *cap = &fimc->vid_cap; + struct fimc_vid_buffer *v_buf; + struct timeval *tv; + struct timespec ts; + + if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { + wake_up(&fimc->irq_queue); + goto done; + } + + if (!list_empty(&cap->active_buf_q) && + test_bit(ST_CAPT_RUN, &fimc->state) && deq_buf) { + ktime_get_real_ts(&ts); + + v_buf = fimc_active_queue_pop(cap); + + tv = &v_buf->vb.v4l2_buf.timestamp; + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + v_buf->vb.v4l2_buf.sequence = cap->frame_count++; + + vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); + } + + if (!list_empty(&cap->pending_buf_q)) { + + v_buf = fimc_pending_queue_pop(cap); + fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); + v_buf->index = cap->buf_index; + + /* Move the buffer to the capture active queue */ + fimc_active_queue_add(cap, v_buf); + + dbg("next frame: %d, done frame: %d", + fimc_hw_get_frame_index(fimc), v_buf->index); + + if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) + cap->buf_index = 0; + } + + if (cap->active_buf_cnt == 0) { + if (deq_buf) + clear_bit(ST_CAPT_RUN, &fimc->state); + + if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) + cap->buf_index = 0; + } else { + set_bit(ST_CAPT_RUN, &fimc->state); + } + + if (test_bit(ST_CAPT_APPLY_CFG, &fimc->state)) + fimc_capture_config_update(cap->ctx); +done: + if (cap->active_buf_cnt == 1) { + fimc_deactivate_capture(fimc); + clear_bit(ST_CAPT_STREAM, &fimc->state); + } + + dbg("frame: %d, active_buf_cnt: %d", + fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); +} + + static int start_streaming(struct vb2_queue *q, unsigned int count) { struct fimc_ctx *ctx = q->drv_priv; @@ -174,9 +249,11 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) vid_cap->frame_count = 0; - ret = fimc_init_capture(fimc); - if (ret) - goto error; + ret = fimc_capture_hw_init(fimc); + if (ret) { + fimc_capture_state_cleanup(fimc, false); + return ret; + } set_bit(ST_CAPT_PEND, &fimc->state); @@ -187,13 +264,10 @@ static int start_streaming(struct vb2_queue *q, unsigned int count) fimc_activate_capture(ctx); if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - fimc_pipeline_s_stream(fimc, 1); + fimc_pipeline_s_stream(&fimc->pipeline, 1); } return 0; -error: - fimc_capture_state_cleanup(fimc, false); - return ret; } static int stop_streaming(struct vb2_queue *q) @@ -214,7 +288,7 @@ int fimc_capture_suspend(struct fimc_dev *fimc) int ret = fimc_stop_capture(fimc, suspend); if (ret) return ret; - return fimc_pipeline_shutdown(fimc); + return fimc_pipeline_shutdown(&fimc->pipeline); } static void buffer_queue(struct vb2_buffer *vb); @@ -230,9 +304,9 @@ int fimc_capture_resume(struct fimc_dev *fimc) INIT_LIST_HEAD(&fimc->vid_cap.active_buf_q); vid_cap->buf_index = 0; - fimc_pipeline_initialize(fimc, &fimc->vid_cap.vfd->entity, + fimc_pipeline_initialize(&fimc->pipeline, &vid_cap->vfd->entity, false); - fimc_init_capture(fimc); + fimc_capture_hw_init(fimc); clear_bit(ST_CAPT_SUSPENDED, &fimc->state); @@ -347,7 +421,7 @@ static void buffer_queue(struct vb2_buffer *vb) spin_unlock_irqrestore(&fimc->slock, flags); if (!test_and_set_bit(ST_CAPT_ISP_STREAM, &fimc->state)) - fimc_pipeline_s_stream(fimc, 1); + fimc_pipeline_s_stream(&fimc->pipeline, 1); return; } spin_unlock_irqrestore(&fimc->slock, flags); @@ -389,15 +463,15 @@ int fimc_capture_ctrls_create(struct fimc_dev *fimc) if (WARN_ON(vid_cap->ctx == NULL)) return -ENXIO; - if (vid_cap->ctx->ctrls_rdy) + if (vid_cap->ctx->ctrls.ready) return 0; ret = fimc_ctrls_create(vid_cap->ctx); - if (ret || vid_cap->user_subdev_api) + if (ret || vid_cap->user_subdev_api || !vid_cap->ctx->ctrls.ready) return ret; - return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrl_handler, - fimc->pipeline.sensor->ctrl_handler); + return v4l2_ctrl_add_handler(&vid_cap->ctx->ctrls.handler, + fimc->pipeline.subdevs[IDX_SENSOR]->ctrl_handler); } static int fimc_capture_set_default_format(struct fimc_dev *fimc); @@ -420,7 +494,7 @@ static int fimc_capture_open(struct file *file) pm_runtime_get_sync(&fimc->pdev->dev); if (++fimc->vid_cap.refcnt == 1) { - ret = fimc_pipeline_initialize(fimc, + ret = fimc_pipeline_initialize(&fimc->pipeline, &fimc->vid_cap.vfd->entity, true); if (ret < 0) { dev_err(&fimc->pdev->dev, @@ -448,7 +522,7 @@ static int fimc_capture_close(struct file *file) if (--fimc->vid_cap.refcnt == 0) { clear_bit(ST_CAPT_BUSY, &fimc->state); fimc_stop_capture(fimc, false); - fimc_pipeline_shutdown(fimc); + fimc_pipeline_shutdown(&fimc->pipeline); clear_bit(ST_CAPT_SUSPENDED, &fimc->state); } @@ -495,7 +569,7 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, { bool rotation = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *var = fimc->variant; + struct fimc_variant *var = fimc->variant; struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *dst = &ctx->d_frame; u32 depth, min_w, max_w, min_h, align_h = 3; @@ -537,8 +611,13 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, } /* Apply the scaler and the output DMA constraints */ max_w = rotation ? pl->out_rot_en_w : pl->out_rot_dis_w; - min_w = ctx->state & FIMC_DST_CROP ? dst->width : var->min_out_pixsize; - min_h = ctx->state & FIMC_DST_CROP ? dst->height : var->min_out_pixsize; + if (ctx->state & FIMC_COMPOSE) { + min_w = dst->offs_h + dst->width; + min_h = dst->offs_v + dst->height; + } else { + min_w = var->min_out_pixsize; + min_h = var->min_out_pixsize; + } if (var->min_vsize_align == 1 && !rotation) align_h = fimc_fmt_is_rgb(ffmt->color) ? 0 : 1; @@ -556,12 +635,13 @@ static struct fimc_fmt *fimc_capture_try_format(struct fimc_ctx *ctx, return ffmt; } -static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, - int pad) +static void fimc_capture_try_selection(struct fimc_ctx *ctx, + struct v4l2_rect *r, + int target) { bool rotate = ctx->rotation == 90 || ctx->rotation == 270; struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *var = fimc->variant; + struct fimc_variant *var = fimc->variant; struct fimc_pix_limit *pl = var->pix_limit; struct fimc_frame *sink = &ctx->s_frame; u32 max_w, max_h, min_w = 0, min_h = 0, min_sz; @@ -575,7 +655,7 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, r->left = r->top = 0; return; } - if (pad == FIMC_SD_PAD_SOURCE) { + if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { if (ctx->rotation != 90 && ctx->rotation != 270) align_h = 1; max_sc_h = min(SCALER_MAX_HRATIO, 1 << (ffs(sink->width) - 3)); @@ -589,8 +669,7 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, max_sc_h = max_sc_v = 1; } /* - * For the crop rectangle at source pad the following constraints - * must be met: + * For the compose rectangle the following constraints must be met: * - it must fit in the sink pad format rectangle (f_width/f_height); * - maximum downscaling ratio is 64; * - maximum crop size depends if the rotator is used or not; @@ -602,7 +681,8 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, rotate ? pl->out_rot_en_w : pl->out_rot_dis_w, rotate ? sink->f_height : sink->f_width); max_h = min_t(u32, FIMC_CAMIF_MAX_HEIGHT, sink->f_height); - if (pad == FIMC_SD_PAD_SOURCE) { + + if (target == V4L2_SEL_TGT_COMPOSE_ACTIVE) { min_w = min_t(u32, max_w, sink->f_width / max_sc_h); min_h = min_t(u32, max_h, sink->f_height / max_sc_v); if (rotate) { @@ -613,13 +693,13 @@ static void fimc_capture_try_crop(struct fimc_ctx *ctx, struct v4l2_rect *r, v4l_bound_align_image(&r->width, min_w, max_w, ffs(min_sz) - 1, &r->height, min_h, max_h, align_h, align_sz); - /* Adjust left/top if cropping rectangle is out of bounds */ + /* Adjust left/top if crop/compose rectangle is out of bounds */ r->left = clamp_t(u32, r->left, 0, sink->f_width - r->width); r->top = clamp_t(u32, r->top, 0, sink->f_height - r->height); r->left = round_down(r->left, var->hor_offs_align); - dbg("pad%d: (%d,%d)/%dx%d, sink fmt: %dx%d", - pad, r->left, r->top, r->width, r->height, + dbg("target %#x: (%d,%d)/%dx%d, sink fmt: %dx%d", + target, r->left, r->top, r->width, r->height, sink->f_width, sink->f_height); } @@ -669,8 +749,8 @@ static int fimc_pipeline_try_format(struct fimc_ctx *ctx, bool set) { struct fimc_dev *fimc = ctx->fimc_dev; - struct v4l2_subdev *sd = fimc->pipeline.sensor; - struct v4l2_subdev *csis = fimc->pipeline.csis; + struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; + struct v4l2_subdev *csis = fimc->pipeline.subdevs[IDX_CSIS]; struct v4l2_subdev_format sfmt; struct v4l2_mbus_framefmt *mf = &sfmt.format; struct fimc_fmt *ffmt = NULL; @@ -851,7 +931,7 @@ static int fimc_capture_set_format(struct fimc_dev *fimc, struct v4l2_format *f) set_frame_bounds(ff, pix->width, pix->height); /* Reset the composition rectangle if not yet configured */ - if (!(ctx->state & FIMC_DST_CROP)) + if (!(ctx->state & FIMC_COMPOSE)) set_frame_crop(ff, 0, 0, pix->width, pix->height); fimc_capture_mark_jpeg_xfer(ctx, fimc_fmt_is_jpeg(ff->fmt->color)); @@ -878,7 +958,7 @@ static int fimc_cap_enum_input(struct file *file, void *priv, struct v4l2_input *i) { struct fimc_dev *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.sensor; + struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; if (i->index != 0) return -EINVAL; @@ -927,7 +1007,7 @@ static int fimc_pipeline_validate(struct fimc_dev *fimc) if (!(pad->flags & MEDIA_PAD_FL_SINK)) break; /* Don't call FIMC subdev operation to avoid nested locking */ - if (sd == fimc->vid_cap.subdev) { + if (sd == &fimc->vid_cap.subdev) { struct fimc_frame *ff = &vid_cap->ctx->s_frame; sink_fmt.format.width = ff->f_width; sink_fmt.format.height = ff->f_height; @@ -970,7 +1050,8 @@ static int fimc_cap_streamon(struct file *file, void *priv, if (fimc_capture_active(fimc)) return -EBUSY; - media_entity_pipeline_start(&p->sensor->entity, p->pipe); + media_entity_pipeline_start(&p->subdevs[IDX_SENSOR]->entity, + p->m_pipeline); if (fimc->vid_cap.user_subdev_api) { ret = fimc_pipeline_validate(fimc); @@ -984,7 +1065,7 @@ static int fimc_cap_streamoff(struct file *file, void *priv, enum v4l2_buf_type type) { struct fimc_dev *fimc = video_drvdata(file); - struct v4l2_subdev *sd = fimc->pipeline.sensor; + struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; int ret; ret = vb2_streamoff(&fimc->vid_cap.vbq, type); @@ -1100,29 +1181,18 @@ static int fimc_cap_s_selection(struct file *file, void *fh, struct v4l2_rect rect = s->r; struct fimc_frame *f; unsigned long flags; - unsigned int pad; if (s->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) return -EINVAL; - switch (s->target) { - case V4L2_SEL_TGT_COMPOSE_DEFAULT: - case V4L2_SEL_TGT_COMPOSE_BOUNDS: - case V4L2_SEL_TGT_COMPOSE_ACTIVE: + if (s->target == V4L2_SEL_TGT_COMPOSE_ACTIVE) f = &ctx->d_frame; - pad = FIMC_SD_PAD_SOURCE; - break; - case V4L2_SEL_TGT_CROP_BOUNDS: - case V4L2_SEL_TGT_CROP_DEFAULT: - case V4L2_SEL_TGT_CROP_ACTIVE: + else if (s->target == V4L2_SEL_TGT_CROP_ACTIVE) f = &ctx->s_frame; - pad = FIMC_SD_PAD_SINK; - break; - default: + else return -EINVAL; - } - fimc_capture_try_crop(ctx, &rect, pad); + fimc_capture_try_selection(ctx, &rect, s->target); if (s->flags & V4L2_SEL_FLAG_LE && !enclosed_rectangle(&rect, &s->r)) @@ -1243,7 +1313,7 @@ void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, struct fimc_vid_buffer, list); vb2_set_plane_payload(&buf->vb, 0, *((u32 *)arg)); } - fimc_capture_irq_handler(fimc, true); + fimc_capture_irq_handler(fimc, 1); fimc_deactivate_capture(fimc); spin_unlock_irqrestore(&fimc->slock, irq_flags); } @@ -1334,77 +1404,122 @@ static int fimc_subdev_set_fmt(struct v4l2_subdev *sd, ff->fmt = ffmt; /* Reset the crop rectangle if required. */ - if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_DST_CROP))) + if (!(fmt->pad == FIMC_SD_PAD_SOURCE && (ctx->state & FIMC_COMPOSE))) set_frame_crop(ff, 0, 0, mf->width, mf->height); if (fmt->pad == FIMC_SD_PAD_SINK) - ctx->state &= ~FIMC_DST_CROP; + ctx->state &= ~FIMC_COMPOSE; mutex_unlock(&fimc->lock); return 0; } -static int fimc_subdev_get_crop(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int fimc_subdev_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct v4l2_rect *r = &crop->rect; - struct fimc_frame *ff; + struct fimc_frame *f = &ctx->s_frame; + struct v4l2_rect *r = &sel->r; + struct v4l2_rect *try_sel; - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { - crop->rect = *v4l2_subdev_get_try_crop(fh, crop->pad); - return 0; - } - ff = crop->pad == FIMC_SD_PAD_SINK ? - &ctx->s_frame : &ctx->d_frame; + if (sel->pad != FIMC_SD_PAD_SINK) + return -EINVAL; mutex_lock(&fimc->lock); - r->left = ff->offs_h; - r->top = ff->offs_v; - r->width = ff->width; - r->height = ff->height; + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + f = &ctx->d_frame; + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + r->width = f->o_width; + r->height = f->o_height; + r->left = 0; + r->top = 0; + mutex_unlock(&fimc->lock); + return 0; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); + f = &ctx->d_frame; + break; + default: + mutex_unlock(&fimc->lock); + return -EINVAL; + } + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + sel->r = *try_sel; + } else { + r->left = f->offs_h; + r->top = f->offs_v; + r->width = f->width; + r->height = f->height; + } + + dbg("target %#x: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d", + sel->pad, r->left, r->top, r->width, r->height, + f->f_width, f->f_height); + mutex_unlock(&fimc->lock); - - dbg("ff:%p, pad%d: l:%d, t:%d, %dx%d, f_w: %d, f_h: %d", - ff, crop->pad, r->left, r->top, r->width, r->height, - ff->f_width, ff->f_height); - return 0; } -static int fimc_subdev_set_crop(struct v4l2_subdev *sd, - struct v4l2_subdev_fh *fh, - struct v4l2_subdev_crop *crop) +static int fimc_subdev_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) { struct fimc_dev *fimc = v4l2_get_subdevdata(sd); struct fimc_ctx *ctx = fimc->vid_cap.ctx; - struct v4l2_rect *r = &crop->rect; - struct fimc_frame *ff; + struct fimc_frame *f = &ctx->s_frame; + struct v4l2_rect *r = &sel->r; + struct v4l2_rect *try_sel; unsigned long flags; - dbg("(%d,%d)/%dx%d", r->left, r->top, r->width, r->height); - - ff = crop->pad == FIMC_SD_PAD_SOURCE ? - &ctx->d_frame : &ctx->s_frame; + if (sel->pad != FIMC_SD_PAD_SINK) + return -EINVAL; mutex_lock(&fimc->lock); - fimc_capture_try_crop(ctx, r, crop->pad); + fimc_capture_try_selection(ctx, r, V4L2_SEL_TGT_CROP_ACTIVE); - if (crop->which == V4L2_SUBDEV_FORMAT_TRY) { + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + f = &ctx->d_frame; + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + r->width = f->o_width; + r->height = f->o_height; + r->left = 0; + r->top = 0; mutex_unlock(&fimc->lock); - *v4l2_subdev_get_try_crop(fh, crop->pad) = *r; return 0; + + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + try_sel = v4l2_subdev_get_try_crop(fh, sel->pad); + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + try_sel = v4l2_subdev_get_try_compose(fh, sel->pad); + f = &ctx->d_frame; + break; + default: + mutex_unlock(&fimc->lock); + return -EINVAL; } - spin_lock_irqsave(&fimc->slock, flags); - set_frame_crop(ff, r->left, r->top, r->width, r->height); - if (crop->pad == FIMC_SD_PAD_SOURCE) - ctx->state |= FIMC_DST_CROP; - set_bit(ST_CAPT_APPLY_CFG, &fimc->state); - spin_unlock_irqrestore(&fimc->slock, flags); + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + *try_sel = sel->r; + } else { + spin_lock_irqsave(&fimc->slock, flags); + set_frame_crop(f, r->left, r->top, r->width, r->height); + set_bit(ST_CAPT_APPLY_CFG, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + if (sel->target == V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL) + ctx->state |= FIMC_COMPOSE; + } - dbg("pad%d: (%d,%d)/%dx%d", crop->pad, r->left, r->top, + dbg("target %#x: (%d,%d)/%dx%d", sel->target, r->left, r->top, r->width, r->height); mutex_unlock(&fimc->lock); @@ -1413,63 +1528,16 @@ static int fimc_subdev_set_crop(struct v4l2_subdev *sd, static struct v4l2_subdev_pad_ops fimc_subdev_pad_ops = { .enum_mbus_code = fimc_subdev_enum_mbus_code, + .get_selection = fimc_subdev_get_selection, + .set_selection = fimc_subdev_set_selection, .get_fmt = fimc_subdev_get_fmt, .set_fmt = fimc_subdev_set_fmt, - .get_crop = fimc_subdev_get_crop, - .set_crop = fimc_subdev_set_crop, }; static struct v4l2_subdev_ops fimc_subdev_ops = { .pad = &fimc_subdev_pad_ops, }; -static int fimc_create_capture_subdev(struct fimc_dev *fimc, - struct v4l2_device *v4l2_dev) -{ - struct v4l2_subdev *sd; - int ret; - - sd = kzalloc(sizeof(*sd), GFP_KERNEL); - if (!sd) - return -ENOMEM; - - v4l2_subdev_init(sd, &fimc_subdev_ops); - sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; - snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); - - fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; - fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; - ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, - fimc->vid_cap.sd_pads, 0); - if (ret) - goto me_err; - ret = v4l2_device_register_subdev(v4l2_dev, sd); - if (ret) - goto sd_err; - - fimc->vid_cap.subdev = sd; - v4l2_set_subdevdata(sd, fimc); - sd->entity.ops = &fimc_sd_media_ops; - return 0; -sd_err: - media_entity_cleanup(&sd->entity); -me_err: - kfree(sd); - return ret; -} - -static void fimc_destroy_capture_subdev(struct fimc_dev *fimc) -{ - struct v4l2_subdev *sd = fimc->vid_cap.subdev; - - if (!sd) - return; - media_entity_cleanup(&sd->entity); - v4l2_device_unregister_subdev(sd); - kfree(sd); - fimc->vid_cap.subdev = NULL; -} - /* Set default format at the sensor and host interface */ static int fimc_capture_set_default_format(struct fimc_dev *fimc) { @@ -1488,7 +1556,7 @@ static int fimc_capture_set_default_format(struct fimc_dev *fimc) } /* fimc->lock must be already initialized */ -int fimc_register_capture_device(struct fimc_dev *fimc, +static int fimc_register_capture_device(struct fimc_dev *fimc, struct v4l2_device *v4l2_dev) { struct video_device *vfd; @@ -1502,11 +1570,11 @@ int fimc_register_capture_device(struct fimc_dev *fimc, return -ENOMEM; ctx->fimc_dev = fimc; - ctx->in_path = FIMC_CAMERA; - ctx->out_path = FIMC_DMA; + ctx->in_path = FIMC_IO_CAMERA; + ctx->out_path = FIMC_IO_DMA; ctx->state = FIMC_CTX_CAP; ctx->s_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); - ctx->d_frame.fmt = fimc_find_format(NULL, NULL, FMT_FLAGS_CAM, 0); + ctx->d_frame.fmt = ctx->s_frame.fmt; vfd = video_device_alloc(); if (!vfd) { @@ -1514,8 +1582,7 @@ int fimc_register_capture_device(struct fimc_dev *fimc, goto err_vd_alloc; } - snprintf(vfd->name, sizeof(vfd->name), "%s.capture", - dev_name(&fimc->pdev->dev)); + snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.capture", fimc->id); vfd->fops = &fimc_capture_fops; vfd->ioctl_ops = &fimc_capture_ioctl_ops; @@ -1523,6 +1590,10 @@ int fimc_register_capture_device(struct fimc_dev *fimc, vfd->minor = -1; vfd->release = video_device_release; vfd->lock = &fimc->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); video_set_drvdata(vfd, fimc); vid_cap = &fimc->vid_cap; @@ -1533,7 +1604,6 @@ int fimc_register_capture_device(struct fimc_dev *fimc, INIT_LIST_HEAD(&vid_cap->pending_buf_q); INIT_LIST_HEAD(&vid_cap->active_buf_q); - spin_lock_init(&ctx->slock); vid_cap->ctx = ctx; q = &fimc->vid_cap.vbq; @@ -1547,18 +1617,22 @@ int fimc_register_capture_device(struct fimc_dev *fimc, vb2_queue_init(q); - fimc->vid_cap.vd_pad.flags = MEDIA_PAD_FL_SINK; - ret = media_entity_init(&vfd->entity, 1, &fimc->vid_cap.vd_pad, 0); + vid_cap->vd_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_init(&vfd->entity, 1, &vid_cap->vd_pad, 0); if (ret) goto err_ent; - ret = fimc_create_capture_subdev(fimc, v4l2_dev); - if (ret) - goto err_sd_reg; - vfd->ctrl_handler = &ctx->ctrl_handler; + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_vd; + + v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", + vfd->name, video_device_node_name(vfd)); + + vfd->ctrl_handler = &ctx->ctrls.handler; return 0; -err_sd_reg: +err_vd: media_entity_cleanup(&vfd->entity); err_ent: video_device_release(vfd); @@ -1567,17 +1641,73 @@ err_vd_alloc: return ret; } -void fimc_unregister_capture_device(struct fimc_dev *fimc) +static int fimc_capture_subdev_registered(struct v4l2_subdev *sd) { - struct video_device *vfd = fimc->vid_cap.vfd; + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + int ret; - if (vfd) { - media_entity_cleanup(&vfd->entity); - /* Can also be called if video device was - not registered */ - video_unregister_device(vfd); + ret = fimc_register_m2m_device(fimc, sd->v4l2_dev); + if (ret) + return ret; + + ret = fimc_register_capture_device(fimc, sd->v4l2_dev); + if (ret) + fimc_unregister_m2m_device(fimc); + + return ret; +} + +static void fimc_capture_subdev_unregistered(struct v4l2_subdev *sd) +{ + struct fimc_dev *fimc = v4l2_get_subdevdata(sd); + + if (fimc == NULL) + return; + + fimc_unregister_m2m_device(fimc); + + if (fimc->vid_cap.vfd) { + media_entity_cleanup(&fimc->vid_cap.vfd->entity); + video_unregister_device(fimc->vid_cap.vfd); + fimc->vid_cap.vfd = NULL; } - fimc_destroy_capture_subdev(fimc); + kfree(fimc->vid_cap.ctx); fimc->vid_cap.ctx = NULL; } + +static const struct v4l2_subdev_internal_ops fimc_capture_sd_internal_ops = { + .registered = fimc_capture_subdev_registered, + .unregistered = fimc_capture_subdev_unregistered, +}; + +int fimc_initialize_capture_subdev(struct fimc_dev *fimc) +{ + struct v4l2_subdev *sd = &fimc->vid_cap.subdev; + int ret; + + v4l2_subdev_init(sd, &fimc_subdev_ops); + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC.%d", fimc->pdev->id); + + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->vid_cap.sd_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->vid_cap.sd_pads, 0); + if (ret) + return ret; + + sd->entity.ops = &fimc_sd_media_ops; + sd->internal_ops = &fimc_capture_sd_internal_ops; + v4l2_set_subdevdata(sd, fimc); + return 0; +} + +void fimc_unregister_capture_subdev(struct fimc_dev *fimc) +{ + struct v4l2_subdev *sd = &fimc->vid_cap.subdev; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_set_subdevdata(sd, NULL); +} diff --git a/drivers/media/video/s5p-fimc/fimc-core.c b/drivers/media/video/s5p-fimc/fimc-core.c index e09ba7b0076e..fedcd561ba27 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.c +++ b/drivers/media/video/s5p-fimc/fimc-core.c @@ -1,8 +1,8 @@ /* - * Samsung S5P/EXYNOS4 SoC series camera interface (video postprocessor) driver + * Samsung S5P/EXYNOS4 SoC series FIMC (CAMIF) driver * - * Copyright (C) 2010-2011 Samsung Electronics Co., Ltd. - * Contact: Sylwester Nawrocki, + * Copyright (C) 2010-2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published @@ -28,6 +28,7 @@ #include #include "fimc-core.h" +#include "fimc-reg.h" #include "fimc-mdevice.h" static char *fimc_clocks[MAX_FIMC_CLOCKS] = { @@ -39,7 +40,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "RGB565", .fourcc = V4L2_PIX_FMT_RGB565, .depth = { 16 }, - .color = S5P_FIMC_RGB565, + .color = FIMC_FMT_RGB565, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M, @@ -47,7 +48,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "BGR666", .fourcc = V4L2_PIX_FMT_BGR666, .depth = { 32 }, - .color = S5P_FIMC_RGB666, + .color = FIMC_FMT_RGB666, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M, @@ -55,7 +56,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "ARGB8888, 32 bpp", .fourcc = V4L2_PIX_FMT_RGB32, .depth = { 32 }, - .color = S5P_FIMC_RGB888, + .color = FIMC_FMT_RGB888, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M | FMT_HAS_ALPHA, @@ -63,7 +64,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "ARGB1555", .fourcc = V4L2_PIX_FMT_RGB555, .depth = { 16 }, - .color = S5P_FIMC_RGB555, + .color = FIMC_FMT_RGB555, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA, @@ -71,7 +72,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "ARGB4444", .fourcc = V4L2_PIX_FMT_RGB444, .depth = { 16 }, - .color = S5P_FIMC_RGB444, + .color = FIMC_FMT_RGB444, .memplanes = 1, .colplanes = 1, .flags = FMT_FLAGS_M2M_OUT | FMT_HAS_ALPHA, @@ -79,7 +80,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 packed, YCbYCr", .fourcc = V4L2_PIX_FMT_YUYV, .depth = { 16 }, - .color = S5P_FIMC_YCBYCR422, + .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, @@ -88,7 +89,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 packed, CbYCrY", .fourcc = V4L2_PIX_FMT_UYVY, .depth = { 16 }, - .color = S5P_FIMC_CBYCRY422, + .color = FIMC_FMT_CBYCRY422, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, @@ -97,7 +98,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 packed, CrYCbY", .fourcc = V4L2_PIX_FMT_VYUY, .depth = { 16 }, - .color = S5P_FIMC_CRYCBY422, + .color = FIMC_FMT_CRYCBY422, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, @@ -106,7 +107,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 packed, YCrYCb", .fourcc = V4L2_PIX_FMT_YVYU, .depth = { 16 }, - .color = S5P_FIMC_YCRYCB422, + .color = FIMC_FMT_YCRYCB422, .memplanes = 1, .colplanes = 1, .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, @@ -115,7 +116,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 planar, Y/Cb/Cr", .fourcc = V4L2_PIX_FMT_YUV422P, .depth = { 12 }, - .color = S5P_FIMC_YCBYCR422, + .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .colplanes = 3, .flags = FMT_FLAGS_M2M, @@ -123,7 +124,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 planar, Y/CbCr", .fourcc = V4L2_PIX_FMT_NV16, .depth = { 16 }, - .color = S5P_FIMC_YCBYCR422, + .color = FIMC_FMT_YCBYCR422, .memplanes = 1, .colplanes = 2, .flags = FMT_FLAGS_M2M, @@ -131,7 +132,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:2 planar, Y/CrCb", .fourcc = V4L2_PIX_FMT_NV61, .depth = { 16 }, - .color = S5P_FIMC_YCRYCB422, + .color = FIMC_FMT_YCRYCB422, .memplanes = 1, .colplanes = 2, .flags = FMT_FLAGS_M2M, @@ -139,7 +140,7 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:0 planar, YCbCr", .fourcc = V4L2_PIX_FMT_YUV420, .depth = { 12 }, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .memplanes = 1, .colplanes = 3, .flags = FMT_FLAGS_M2M, @@ -147,14 +148,14 @@ static struct fimc_fmt fimc_formats[] = { .name = "YUV 4:2:0 planar, Y/CbCr", .fourcc = V4L2_PIX_FMT_NV12, .depth = { 12 }, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .memplanes = 1, .colplanes = 2, .flags = FMT_FLAGS_M2M, }, { .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr", .fourcc = V4L2_PIX_FMT_NV12M, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .depth = { 8, 4 }, .memplanes = 2, .colplanes = 2, @@ -162,7 +163,7 @@ static struct fimc_fmt fimc_formats[] = { }, { .name = "YUV 4:2:0 non-contiguous 3-planar, Y/Cb/Cr", .fourcc = V4L2_PIX_FMT_YUV420M, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .depth = { 8, 2, 2 }, .memplanes = 3, .colplanes = 3, @@ -170,7 +171,7 @@ static struct fimc_fmt fimc_formats[] = { }, { .name = "YUV 4:2:0 non-contiguous 2-planar, Y/CbCr, tiled", .fourcc = V4L2_PIX_FMT_NV12MT, - .color = S5P_FIMC_YCBCR420, + .color = FIMC_FMT_YCBCR420, .depth = { 8, 4 }, .memplanes = 2, .colplanes = 2, @@ -178,7 +179,7 @@ static struct fimc_fmt fimc_formats[] = { }, { .name = "JPEG encoded data", .fourcc = V4L2_PIX_FMT_JPEG, - .color = S5P_FIMC_JPEG, + .color = FIMC_FMT_JPEG, .depth = { 8 }, .memplanes = 1, .colplanes = 1, @@ -187,12 +188,12 @@ static struct fimc_fmt fimc_formats[] = { }, }; -static unsigned int get_m2m_fmt_flags(unsigned int stream_type) +struct fimc_fmt *fimc_get_format(unsigned int index) { - if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - return FMT_FLAGS_M2M_IN; - else - return FMT_FLAGS_M2M_OUT; + if (index >= ARRAY_SIZE(fimc_formats)) + return NULL; + + return &fimc_formats[index]; } int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, @@ -230,7 +231,7 @@ static int fimc_get_scaler_factor(u32 src, u32 tar, u32 *ratio, u32 *shift) int fimc_set_scaler_info(struct fimc_ctx *ctx) { - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + struct fimc_variant *variant = ctx->fimc_dev->variant; struct device *dev = &ctx->fimc_dev->pdev->dev; struct fimc_scaler *sc = &ctx->scaler; struct fimc_frame *s_frame = &ctx->s_frame; @@ -293,126 +294,9 @@ int fimc_set_scaler_info(struct fimc_ctx *ctx) return 0; } -static void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) -{ - struct vb2_buffer *src_vb, *dst_vb; - - if (!ctx || !ctx->m2m_ctx) - return; - - src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); - dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - - if (src_vb && dst_vb) { - v4l2_m2m_buf_done(src_vb, vb_state); - v4l2_m2m_buf_done(dst_vb, vb_state); - v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, - ctx->m2m_ctx); - } -} - -/* Complete the transaction which has been scheduled for execution. */ -static int fimc_m2m_shutdown(struct fimc_ctx *ctx) -{ - struct fimc_dev *fimc = ctx->fimc_dev; - int ret; - - if (!fimc_m2m_pending(fimc)) - return 0; - - fimc_ctx_state_lock_set(FIMC_CTX_SHUT, ctx); - - ret = wait_event_timeout(fimc->irq_queue, - !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), - FIMC_SHUTDOWN_TIMEOUT); - - return ret == 0 ? -ETIMEDOUT : ret; -} - -static int start_streaming(struct vb2_queue *q, unsigned int count) -{ - struct fimc_ctx *ctx = q->drv_priv; - int ret; - - ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); - return ret > 0 ? 0 : ret; -} - -static int stop_streaming(struct vb2_queue *q) -{ - struct fimc_ctx *ctx = q->drv_priv; - int ret; - - ret = fimc_m2m_shutdown(ctx); - if (ret == -ETIMEDOUT) - fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); - - pm_runtime_put(&ctx->fimc_dev->pdev->dev); - return 0; -} - -void fimc_capture_irq_handler(struct fimc_dev *fimc, bool final) -{ - struct fimc_vid_cap *cap = &fimc->vid_cap; - struct fimc_vid_buffer *v_buf; - struct timeval *tv; - struct timespec ts; - - if (test_and_clear_bit(ST_CAPT_SHUT, &fimc->state)) { - wake_up(&fimc->irq_queue); - return; - } - - if (!list_empty(&cap->active_buf_q) && - test_bit(ST_CAPT_RUN, &fimc->state) && final) { - ktime_get_real_ts(&ts); - - v_buf = fimc_active_queue_pop(cap); - - tv = &v_buf->vb.v4l2_buf.timestamp; - tv->tv_sec = ts.tv_sec; - tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; - v_buf->vb.v4l2_buf.sequence = cap->frame_count++; - - vb2_buffer_done(&v_buf->vb, VB2_BUF_STATE_DONE); - } - - if (!list_empty(&cap->pending_buf_q)) { - - v_buf = fimc_pending_queue_pop(cap); - fimc_hw_set_output_addr(fimc, &v_buf->paddr, cap->buf_index); - v_buf->index = cap->buf_index; - - /* Move the buffer to the capture active queue */ - fimc_active_queue_add(cap, v_buf); - - dbg("next frame: %d, done frame: %d", - fimc_hw_get_frame_index(fimc), v_buf->index); - - if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) - cap->buf_index = 0; - } - - if (cap->active_buf_cnt == 0) { - if (final) - clear_bit(ST_CAPT_RUN, &fimc->state); - - if (++cap->buf_index >= FIMC_MAX_OUT_BUFS) - cap->buf_index = 0; - } else { - set_bit(ST_CAPT_RUN, &fimc->state); - } - - fimc_capture_config_update(cap->ctx); - - dbg("frame: %d, active_buf_cnt: %d", - fimc_hw_get_frame_index(fimc), cap->active_buf_cnt); -} - static irqreturn_t fimc_irq_handler(int irq, void *priv) { struct fimc_dev *fimc = priv; - struct fimc_vid_cap *cap = &fimc->vid_cap; struct fimc_ctx *ctx; fimc_hw_clear_irq(fimc); @@ -430,21 +314,16 @@ static irqreturn_t fimc_irq_handler(int irq, void *priv) spin_unlock(&fimc->slock); fimc_m2m_job_finish(ctx, VB2_BUF_STATE_DONE); - spin_lock(&ctx->slock); if (ctx->state & FIMC_CTX_SHUT) { ctx->state &= ~FIMC_CTX_SHUT; wake_up(&fimc->irq_queue); } - spin_unlock(&ctx->slock); + return IRQ_HANDLED; } - return IRQ_HANDLED; } else if (test_bit(ST_CAPT_PEND, &fimc->state)) { - fimc_capture_irq_handler(fimc, - !test_bit(ST_CAPT_JPEG, &fimc->state)); - if (cap->active_buf_cnt == 1) { - fimc_deactivate_capture(fimc); - clear_bit(ST_CAPT_STREAM, &fimc->state); - } + int last_buf = test_bit(ST_CAPT_JPEG, &fimc->state) && + fimc->vid_cap.reqbufs_count == 1; + fimc_capture_irq_handler(fimc, !last_buf); } out: spin_unlock(&fimc->slock); @@ -482,7 +361,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, case 3: paddr->cb = (u32)(paddr->y + pix_size); /* decompose Y into Y/Cb/Cr */ - if (S5P_FIMC_YCBCR420 == frame->fmt->color) + if (FIMC_FMT_YCBCR420 == frame->fmt->color) paddr->cr = (u32)(paddr->cb + (pix_size >> 2)); else /* 422 */ @@ -510,40 +389,40 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, void fimc_set_yuv_order(struct fimc_ctx *ctx) { /* The one only mode supported in SoC. */ - ctx->in_order_2p = S5P_FIMC_LSB_CRCB; - ctx->out_order_2p = S5P_FIMC_LSB_CRCB; + ctx->in_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB; + ctx->out_order_2p = FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB; /* Set order for 1 plane input formats. */ switch (ctx->s_frame.fmt->color) { - case S5P_FIMC_YCRYCB422: - ctx->in_order_1p = S5P_MSCTRL_ORDER422_CBYCRY; + case FIMC_FMT_YCRYCB422: + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CBYCRY; break; - case S5P_FIMC_CBYCRY422: - ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCRYCB; + case FIMC_FMT_CBYCRY422: + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCRYCB; break; - case S5P_FIMC_CRYCBY422: - ctx->in_order_1p = S5P_MSCTRL_ORDER422_YCBYCR; + case FIMC_FMT_CRYCBY422: + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_YCBYCR; break; - case S5P_FIMC_YCBYCR422: + case FIMC_FMT_YCBYCR422: default: - ctx->in_order_1p = S5P_MSCTRL_ORDER422_CRYCBY; + ctx->in_order_1p = FIMC_REG_MSCTRL_ORDER422_CRYCBY; break; } dbg("ctx->in_order_1p= %d", ctx->in_order_1p); switch (ctx->d_frame.fmt->color) { - case S5P_FIMC_YCRYCB422: - ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CBYCRY; + case FIMC_FMT_YCRYCB422: + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CBYCRY; break; - case S5P_FIMC_CBYCRY422: - ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCRYCB; + case FIMC_FMT_CBYCRY422: + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCRYCB; break; - case S5P_FIMC_CRYCBY422: - ctx->out_order_1p = S5P_CIOCTRL_ORDER422_YCBYCR; + case FIMC_FMT_CRYCBY422: + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_YCBYCR; break; - case S5P_FIMC_YCBYCR422: + case FIMC_FMT_YCBYCR422: default: - ctx->out_order_1p = S5P_CIOCTRL_ORDER422_CRYCBY; + ctx->out_order_1p = FIMC_REG_CIOCTRL_ORDER422_CRYCBY; break; } dbg("ctx->out_order_1p= %d", ctx->out_order_1p); @@ -551,7 +430,7 @@ void fimc_set_yuv_order(struct fimc_ctx *ctx) void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) { - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + struct fimc_variant *variant = ctx->fimc_dev->variant; u32 i, depth = 0; for (i = 0; i < f->fmt->colplanes; i++) @@ -574,7 +453,7 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->dma_offset.cb_h >>= 1; f->dma_offset.cr_h >>= 1; } - if (f->fmt->color == S5P_FIMC_YCBCR420) { + if (f->fmt->color == FIMC_FMT_YCBCR420) { f->dma_offset.cb_v >>= 1; f->dma_offset.cr_v >>= 1; } @@ -584,203 +463,58 @@ void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f) f->fmt->color, f->dma_offset.y_h, f->dma_offset.y_v); } -/** - * fimc_prepare_config - check dimensions, operation and color mode - * and pre-calculate offset and the scaling coefficients. - * - * @ctx: hardware context information - * @flags: flags indicating which parameters to check/update - * - * Return: 0 if dimensions are valid or non zero otherwise. - */ -int fimc_prepare_config(struct fimc_ctx *ctx, u32 flags) +int fimc_set_color_effect(struct fimc_ctx *ctx, enum v4l2_colorfx colorfx) { - struct fimc_frame *s_frame, *d_frame; - struct vb2_buffer *vb = NULL; - int ret = 0; + struct fimc_effect *effect = &ctx->effect; - s_frame = &ctx->s_frame; - d_frame = &ctx->d_frame; - - if (flags & FIMC_PARAMS) { - /* Prepare the DMA offset ratios for scaler. */ - fimc_prepare_dma_offset(ctx, &ctx->s_frame); - fimc_prepare_dma_offset(ctx, &ctx->d_frame); - - if (s_frame->height > (SCALER_MAX_VRATIO * d_frame->height) || - s_frame->width > (SCALER_MAX_HRATIO * d_frame->width)) { - err("out of scaler range"); - return -EINVAL; - } - fimc_set_yuv_order(ctx); - } - - if (flags & FIMC_SRC_ADDR) { - vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, s_frame, &s_frame->paddr); - if (ret) - return ret; - } - - if (flags & FIMC_DST_ADDR) { - vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); - ret = fimc_prepare_addr(ctx, vb, d_frame, &d_frame->paddr); - } - - return ret; -} - -static void fimc_dma_run(void *priv) -{ - struct fimc_ctx *ctx = priv; - struct fimc_dev *fimc; - unsigned long flags; - u32 ret; - - if (WARN(!ctx, "null hardware context\n")) - return; - - fimc = ctx->fimc_dev; - spin_lock_irqsave(&fimc->slock, flags); - set_bit(ST_M2M_PEND, &fimc->state); - - spin_lock(&ctx->slock); - ctx->state |= (FIMC_SRC_ADDR | FIMC_DST_ADDR); - ret = fimc_prepare_config(ctx, ctx->state); - if (ret) - goto dma_unlock; - - /* Reconfigure hardware if the context has changed. */ - if (fimc->m2m.ctx != ctx) { - ctx->state |= FIMC_PARAMS; - fimc->m2m.ctx = ctx; - } - fimc_hw_set_input_addr(fimc, &ctx->s_frame.paddr); - - if (ctx->state & FIMC_PARAMS) { - fimc_hw_set_input_path(ctx); - fimc_hw_set_in_dma(ctx); - ret = fimc_set_scaler_info(ctx); - if (ret) { - spin_unlock(&fimc->slock); - goto dma_unlock; - } - fimc_hw_set_prescaler(ctx); - fimc_hw_set_mainscaler(ctx); - fimc_hw_set_target_format(ctx); - fimc_hw_set_rotation(ctx); - fimc_hw_set_effect(ctx, false); - } - - fimc_hw_set_output_path(ctx); - if (ctx->state & (FIMC_DST_ADDR | FIMC_PARAMS)) - fimc_hw_set_output_addr(fimc, &ctx->d_frame.paddr, -1); - - if (ctx->state & FIMC_PARAMS) { - fimc_hw_set_out_dma(ctx); - if (fimc->variant->has_alpha) - fimc_hw_set_rgb_alpha(ctx); - } - - fimc_activate_capture(ctx); - - ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | - FIMC_SRC_FMT | FIMC_DST_FMT); - fimc_hw_activate_input_dma(fimc, true); -dma_unlock: - spin_unlock(&ctx->slock); - spin_unlock_irqrestore(&fimc->slock, flags); -} - -static void fimc_job_abort(void *priv) -{ - fimc_m2m_shutdown(priv); -} - -static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, - unsigned int *num_buffers, unsigned int *num_planes, - unsigned int sizes[], void *allocators[]) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - struct fimc_frame *f; - int i; - - f = ctx_get_frame(ctx, vq->type); - if (IS_ERR(f)) - return PTR_ERR(f); - /* - * Return number of non-contigous planes (plane buffers) - * depending on the configured color format. - */ - if (!f->fmt) + switch (colorfx) { + case V4L2_COLORFX_NONE: + effect->type = FIMC_REG_CIIMGEFF_FIN_BYPASS; + break; + case V4L2_COLORFX_BW: + effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; + effect->pat_cb = 128; + effect->pat_cr = 128; + break; + case V4L2_COLORFX_SEPIA: + effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; + effect->pat_cb = 115; + effect->pat_cr = 145; + break; + case V4L2_COLORFX_NEGATIVE: + effect->type = FIMC_REG_CIIMGEFF_FIN_NEGATIVE; + break; + case V4L2_COLORFX_EMBOSS: + effect->type = FIMC_REG_CIIMGEFF_FIN_EMBOSSING; + break; + case V4L2_COLORFX_ART_FREEZE: + effect->type = FIMC_REG_CIIMGEFF_FIN_ARTFREEZE; + break; + case V4L2_COLORFX_SILHOUETTE: + effect->type = FIMC_REG_CIIMGEFF_FIN_SILHOUETTE; + break; + case V4L2_COLORFX_SET_CBCR: + effect->type = FIMC_REG_CIIMGEFF_FIN_ARBITRARY; + effect->pat_cb = ctx->ctrls.colorfx_cbcr->val >> 8; + effect->pat_cr = ctx->ctrls.colorfx_cbcr->val & 0xff; + break; + default: return -EINVAL; - - *num_planes = f->fmt->memplanes; - for (i = 0; i < f->fmt->memplanes; i++) { - sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; - allocators[i] = ctx->fimc_dev->alloc_ctx; } - return 0; -} - -static int fimc_buf_prepare(struct vb2_buffer *vb) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - struct fimc_frame *frame; - int i; - - frame = ctx_get_frame(ctx, vb->vb2_queue->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); - - for (i = 0; i < frame->fmt->memplanes; i++) - vb2_set_plane_payload(vb, i, frame->payload[i]); return 0; } -static void fimc_buf_queue(struct vb2_buffer *vb) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); - - dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); - - if (ctx->m2m_ctx) - v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); -} - -static void fimc_lock(struct vb2_queue *vq) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_lock(&ctx->fimc_dev->lock); -} - -static void fimc_unlock(struct vb2_queue *vq) -{ - struct fimc_ctx *ctx = vb2_get_drv_priv(vq); - mutex_unlock(&ctx->fimc_dev->lock); -} - -static struct vb2_ops fimc_qops = { - .queue_setup = fimc_queue_setup, - .buf_prepare = fimc_buf_prepare, - .buf_queue = fimc_buf_queue, - .wait_prepare = fimc_unlock, - .wait_finish = fimc_lock, - .stop_streaming = stop_streaming, - .start_streaming = start_streaming, -}; - /* * V4L2 controls handling */ #define ctrl_to_ctx(__ctrl) \ - container_of((__ctrl)->handler, struct fimc_ctx, ctrl_handler) + container_of((__ctrl)->handler, struct fimc_ctx, ctrls.handler) static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) { struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *variant = fimc->variant; + struct fimc_variant *variant = fimc->variant; unsigned int flags = FIMC_DST_FMT | FIMC_SRC_FMT; int ret = 0; @@ -815,7 +549,14 @@ static int __fimc_s_ctrl(struct fimc_ctx *ctx, struct v4l2_ctrl *ctrl) case V4L2_CID_ALPHA_COMPONENT: ctx->d_frame.alpha = ctrl->val; break; + + case V4L2_CID_COLORFX: + ret = fimc_set_color_effect(ctx, ctrl->val); + if (ret) + return ret; + break; } + ctx->state |= FIMC_PARAMS; set_bit(ST_CAPT_APPLY_CFG, &fimc->state); return 0; @@ -827,9 +568,9 @@ static int fimc_s_ctrl(struct v4l2_ctrl *ctrl) unsigned long flags; int ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ret = __fimc_s_ctrl(ctx, ctrl); - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); return ret; } @@ -840,71 +581,93 @@ static const struct v4l2_ctrl_ops fimc_ctrl_ops = { int fimc_ctrls_create(struct fimc_ctx *ctx) { - struct samsung_fimc_variant *variant = ctx->fimc_dev->variant; + struct fimc_variant *variant = ctx->fimc_dev->variant; unsigned int max_alpha = fimc_get_alpha_mask(ctx->d_frame.fmt); + struct fimc_ctrls *ctrls = &ctx->ctrls; + struct v4l2_ctrl_handler *handler = &ctrls->handler; - if (ctx->ctrls_rdy) + if (ctx->ctrls.ready) return 0; - v4l2_ctrl_handler_init(&ctx->ctrl_handler, 4); - ctx->ctrl_rotate = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + v4l2_ctrl_handler_init(handler, 6); + + ctrls->rotate = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0); - ctx->ctrl_hflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + ctrls->hflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0); - ctx->ctrl_vflip = v4l2_ctrl_new_std(&ctx->ctrl_handler, &fimc_ctrl_ops, + ctrls->vflip = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0); + if (variant->has_alpha) - ctx->ctrl_alpha = v4l2_ctrl_new_std(&ctx->ctrl_handler, - &fimc_ctrl_ops, V4L2_CID_ALPHA_COMPONENT, - 0, max_alpha, 1, 0); + ctrls->alpha = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, + 0, max_alpha, 1, 0); else - ctx->ctrl_alpha = NULL; + ctrls->alpha = NULL; - ctx->ctrls_rdy = ctx->ctrl_handler.error == 0; + ctrls->colorfx = v4l2_ctrl_new_std_menu(handler, &fimc_ctrl_ops, + V4L2_CID_COLORFX, V4L2_COLORFX_SET_CBCR, + ~0x983f, V4L2_COLORFX_NONE); - return ctx->ctrl_handler.error; + ctrls->colorfx_cbcr = v4l2_ctrl_new_std(handler, &fimc_ctrl_ops, + V4L2_CID_COLORFX_CBCR, 0, 0xffff, 1, 0); + + ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS; + + if (!handler->error) { + v4l2_ctrl_cluster(3, &ctrls->colorfx); + ctrls->ready = true; + } + + return handler->error; } void fimc_ctrls_delete(struct fimc_ctx *ctx) { - if (ctx->ctrls_rdy) { - v4l2_ctrl_handler_free(&ctx->ctrl_handler); - ctx->ctrls_rdy = false; - ctx->ctrl_alpha = NULL; + struct fimc_ctrls *ctrls = &ctx->ctrls; + + if (ctrls->ready) { + v4l2_ctrl_handler_free(&ctrls->handler); + ctrls->ready = false; + ctrls->alpha = NULL; } } void fimc_ctrls_activate(struct fimc_ctx *ctx, bool active) { unsigned int has_alpha = ctx->d_frame.fmt->flags & FMT_HAS_ALPHA; + struct fimc_ctrls *ctrls = &ctx->ctrls; - if (!ctx->ctrls_rdy) + if (!ctrls->ready) return; - mutex_lock(&ctx->ctrl_handler.lock); - v4l2_ctrl_activate(ctx->ctrl_rotate, active); - v4l2_ctrl_activate(ctx->ctrl_hflip, active); - v4l2_ctrl_activate(ctx->ctrl_vflip, active); - if (ctx->ctrl_alpha) - v4l2_ctrl_activate(ctx->ctrl_alpha, active && has_alpha); + mutex_lock(&ctrls->handler.lock); + v4l2_ctrl_activate(ctrls->rotate, active); + v4l2_ctrl_activate(ctrls->hflip, active); + v4l2_ctrl_activate(ctrls->vflip, active); + v4l2_ctrl_activate(ctrls->colorfx, active); + if (ctrls->alpha) + v4l2_ctrl_activate(ctrls->alpha, active && has_alpha); if (active) { - ctx->rotation = ctx->ctrl_rotate->val; - ctx->hflip = ctx->ctrl_hflip->val; - ctx->vflip = ctx->ctrl_vflip->val; + fimc_set_color_effect(ctx, ctrls->colorfx->cur.val); + ctx->rotation = ctrls->rotate->val; + ctx->hflip = ctrls->hflip->val; + ctx->vflip = ctrls->vflip->val; } else { + ctx->effect.type = FIMC_REG_CIIMGEFF_FIN_BYPASS; ctx->rotation = 0; ctx->hflip = 0; ctx->vflip = 0; } - mutex_unlock(&ctx->ctrl_handler.lock); + mutex_unlock(&ctrls->handler.lock); } /* Update maximum value of the alpha color control */ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) { struct fimc_dev *fimc = ctx->fimc_dev; - struct v4l2_ctrl *ctrl = ctx->ctrl_alpha; + struct v4l2_ctrl *ctrl = ctx->ctrls.alpha; if (ctrl == NULL || !fimc->variant->has_alpha) return; @@ -918,39 +681,6 @@ void fimc_alpha_ctrl_update(struct fimc_ctx *ctx) v4l2_ctrl_unlock(ctrl); } -/* - * V4L2 ioctl handlers - */ -static int fimc_m2m_querycap(struct file *file, void *fh, - struct v4l2_capability *cap) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_dev *fimc = ctx->fimc_dev; - - strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); - strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); - cap->bus_info[0] = 0; - cap->capabilities = V4L2_CAP_STREAMING | - V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; - - return 0; -} - -static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - struct fimc_fmt *fmt; - - fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), - f->index); - if (!fmt) - return -EINVAL; - - strncpy(f->description, fmt->name, sizeof(f->description) - 1); - f->pixelformat = fmt->fourcc; - return 0; -} - int fimc_fill_format(struct fimc_frame *frame, struct v4l2_format *f) { struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; @@ -1029,18 +759,6 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, } } -static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_frame *frame = ctx_get_frame(ctx, f->type); - - if (IS_ERR(frame)) - return PTR_ERR(frame); - - return fimc_fill_format(frame, f); -} - /** * fimc_find_format - lookup fimc color format by fourcc or media bus format * @pixelformat: fourcc to match, ignored if null @@ -1073,535 +791,10 @@ struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, return def_fmt; } -static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) -{ - struct fimc_dev *fimc = ctx->fimc_dev; - struct samsung_fimc_variant *variant = fimc->variant; - struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; - struct fimc_fmt *fmt; - u32 max_w, mod_x, mod_y; - - if (!IS_M2M(f->type)) - return -EINVAL; - - dbg("w: %d, h: %d", pix->width, pix->height); - - fmt = fimc_find_format(&pix->pixelformat, NULL, - get_m2m_fmt_flags(f->type), 0); - if (WARN(fmt == NULL, "Pixel format lookup failed")) - return -EINVAL; - - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_NONE; - else if (pix->field != V4L2_FIELD_NONE) - return -EINVAL; - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - max_w = variant->pix_limit->scaler_dis_w; - mod_x = ffs(variant->min_inp_pixsize) - 1; - } else { - max_w = variant->pix_limit->out_rot_dis_w; - mod_x = ffs(variant->min_out_pixsize) - 1; - } - - if (tiled_fmt(fmt)) { - mod_x = 6; /* 64 x 32 pixels tile */ - mod_y = 5; - } else { - if (variant->min_vsize_align == 1) - mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; - else - mod_y = ffs(variant->min_vsize_align) - 1; - } - - v4l_bound_align_image(&pix->width, 16, max_w, mod_x, - &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); - - fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); - return 0; -} - -static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return fimc_try_fmt_mplane(ctx, f); -} - -static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, - struct v4l2_format *f) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_dev *fimc = ctx->fimc_dev; - struct vb2_queue *vq; - struct fimc_frame *frame; - struct v4l2_pix_format_mplane *pix; - int i, ret = 0; - - ret = fimc_try_fmt_mplane(ctx, f); - if (ret) - return ret; - - vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); - - if (vb2_is_busy(vq)) { - v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type); - return -EBUSY; - } - - if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - frame = &ctx->s_frame; - else - frame = &ctx->d_frame; - - pix = &f->fmt.pix_mp; - frame->fmt = fimc_find_format(&pix->pixelformat, NULL, - get_m2m_fmt_flags(f->type), 0); - if (!frame->fmt) - return -EINVAL; - - /* Update RGB Alpha control state and value range */ - fimc_alpha_ctrl_update(ctx); - - for (i = 0; i < frame->fmt->colplanes; i++) { - frame->payload[i] = - (pix->width * pix->height * frame->fmt->depth[i]) / 8; - } - - fimc_fill_frame(frame, f); - - ctx->scaler.enabled = 1; - - if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); - else - fimc_ctx_state_lock_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); - - dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); - - return 0; -} - -static int fimc_m2m_reqbufs(struct file *file, void *fh, - struct v4l2_requestbuffers *reqbufs) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); -} - -static int fimc_m2m_querybuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_qbuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_dqbuf(struct file *file, void *fh, - struct v4l2_buffer *buf) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); -} - -static int fimc_m2m_streamon(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - /* The source and target color format need to be set */ - if (V4L2_TYPE_IS_OUTPUT(type)) { - if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) - return -EINVAL; - } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { - return -EINVAL; - } - - return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); -} - -static int fimc_m2m_streamoff(struct file *file, void *fh, - enum v4l2_buf_type type) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - - return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); -} - -static int fimc_m2m_cropcap(struct file *file, void *fh, - struct v4l2_cropcap *cr) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_frame *frame; - - frame = ctx_get_frame(ctx, cr->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); - - cr->bounds.left = 0; - cr->bounds.top = 0; - cr->bounds.width = frame->o_width; - cr->bounds.height = frame->o_height; - cr->defrect = cr->bounds; - - return 0; -} - -static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_frame *frame; - - frame = ctx_get_frame(ctx, cr->type); - if (IS_ERR(frame)) - return PTR_ERR(frame); - - cr->c.left = frame->offs_h; - cr->c.top = frame->offs_v; - cr->c.width = frame->width; - cr->c.height = frame->height; - - return 0; -} - -static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) -{ - struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_frame *f; - u32 min_size, halign, depth = 0; - int i; - - if (cr->c.top < 0 || cr->c.left < 0) { - v4l2_err(fimc->m2m.vfd, - "doesn't support negative values for top & left\n"); - return -EINVAL; - } - if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) - f = &ctx->d_frame; - else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) - f = &ctx->s_frame; - else - return -EINVAL; - - min_size = (f == &ctx->s_frame) ? - fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; - - /* Get pixel alignment constraints. */ - if (fimc->variant->min_vsize_align == 1) - halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; - else - halign = ffs(fimc->variant->min_vsize_align) - 1; - - for (i = 0; i < f->fmt->colplanes; i++) - depth += f->fmt->depth[i]; - - v4l_bound_align_image(&cr->c.width, min_size, f->o_width, - ffs(min_size) - 1, - &cr->c.height, min_size, f->o_height, - halign, 64/(ALIGN(depth, 8))); - - /* adjust left/top if cropping rectangle is out of bounds */ - if (cr->c.left + cr->c.width > f->o_width) - cr->c.left = f->o_width - cr->c.width; - if (cr->c.top + cr->c.height > f->o_height) - cr->c.top = f->o_height - cr->c.height; - - cr->c.left = round_down(cr->c.left, min_size); - cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); - - dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", - cr->c.left, cr->c.top, cr->c.width, cr->c.height, - f->f_width, f->f_height); - - return 0; -} - -static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) -{ - struct fimc_ctx *ctx = fh_to_ctx(fh); - struct fimc_dev *fimc = ctx->fimc_dev; - struct fimc_frame *f; - int ret; - - ret = fimc_m2m_try_crop(ctx, cr); - if (ret) - return ret; - - f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? - &ctx->s_frame : &ctx->d_frame; - - /* Check to see if scaling ratio is within supported range */ - if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { - if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { - ret = fimc_check_scaler_ratio(ctx, cr->c.width, - cr->c.height, ctx->d_frame.width, - ctx->d_frame.height, ctx->rotation); - } else { - ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, - ctx->s_frame.height, cr->c.width, - cr->c.height, ctx->rotation); - } - if (ret) { - v4l2_err(fimc->m2m.vfd, "Out of scaler range\n"); - return -EINVAL; - } - } - - f->offs_h = cr->c.left; - f->offs_v = cr->c.top; - f->width = cr->c.width; - f->height = cr->c.height; - - fimc_ctx_state_lock_set(FIMC_PARAMS, ctx); - - return 0; -} - -static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { - .vidioc_querycap = fimc_m2m_querycap, - - .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, - .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, - - .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, - .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, - - .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, - .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, - - .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, - .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, - - .vidioc_reqbufs = fimc_m2m_reqbufs, - .vidioc_querybuf = fimc_m2m_querybuf, - - .vidioc_qbuf = fimc_m2m_qbuf, - .vidioc_dqbuf = fimc_m2m_dqbuf, - - .vidioc_streamon = fimc_m2m_streamon, - .vidioc_streamoff = fimc_m2m_streamoff, - - .vidioc_g_crop = fimc_m2m_g_crop, - .vidioc_s_crop = fimc_m2m_s_crop, - .vidioc_cropcap = fimc_m2m_cropcap - -}; - -static int queue_init(void *priv, struct vb2_queue *src_vq, - struct vb2_queue *dst_vq) -{ - struct fimc_ctx *ctx = priv; - int ret; - - memset(src_vq, 0, sizeof(*src_vq)); - src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - src_vq->io_modes = VB2_MMAP | VB2_USERPTR; - src_vq->drv_priv = ctx; - src_vq->ops = &fimc_qops; - src_vq->mem_ops = &vb2_dma_contig_memops; - src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - - ret = vb2_queue_init(src_vq); - if (ret) - return ret; - - memset(dst_vq, 0, sizeof(*dst_vq)); - dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; - dst_vq->drv_priv = ctx; - dst_vq->ops = &fimc_qops; - dst_vq->mem_ops = &vb2_dma_contig_memops; - dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); - - return vb2_queue_init(dst_vq); -} - -static int fimc_m2m_open(struct file *file) -{ - struct fimc_dev *fimc = video_drvdata(file); - struct fimc_ctx *ctx; - int ret; - - dbg("pid: %d, state: 0x%lx, refcnt: %d", - task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); - - /* - * Return if the corresponding video capture node - * is already opened. - */ - if (fimc->vid_cap.refcnt > 0) - return -EBUSY; - - ctx = kzalloc(sizeof *ctx, GFP_KERNEL); - if (!ctx) - return -ENOMEM; - v4l2_fh_init(&ctx->fh, fimc->m2m.vfd); - ctx->fimc_dev = fimc; - - /* Default color format */ - ctx->s_frame.fmt = &fimc_formats[0]; - ctx->d_frame.fmt = &fimc_formats[0]; - - ret = fimc_ctrls_create(ctx); - if (ret) - goto error_fh; - - /* Use separate control handler per file handle */ - ctx->fh.ctrl_handler = &ctx->ctrl_handler; - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - - /* Setup the device context for memory-to-memory mode */ - ctx->state = FIMC_CTX_M2M; - ctx->flags = 0; - ctx->in_path = FIMC_DMA; - ctx->out_path = FIMC_DMA; - spin_lock_init(&ctx->slock); - - ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); - if (IS_ERR(ctx->m2m_ctx)) { - ret = PTR_ERR(ctx->m2m_ctx); - goto error_c; - } - - if (fimc->m2m.refcnt++ == 0) - set_bit(ST_M2M_RUN, &fimc->state); - return 0; - -error_c: - fimc_ctrls_delete(ctx); -error_fh: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - kfree(ctx); - return ret; -} - -static int fimc_m2m_release(struct file *file) -{ - struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - struct fimc_dev *fimc = ctx->fimc_dev; - - dbg("pid: %d, state: 0x%lx, refcnt= %d", - task_pid_nr(current), fimc->state, fimc->m2m.refcnt); - - v4l2_m2m_ctx_release(ctx->m2m_ctx); - fimc_ctrls_delete(ctx); - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - - if (--fimc->m2m.refcnt <= 0) - clear_bit(ST_M2M_RUN, &fimc->state); - kfree(ctx); - return 0; -} - -static unsigned int fimc_m2m_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - - return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); -} - - -static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct fimc_ctx *ctx = fh_to_ctx(file->private_data); - - return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); -} - -static const struct v4l2_file_operations fimc_m2m_fops = { - .owner = THIS_MODULE, - .open = fimc_m2m_open, - .release = fimc_m2m_release, - .poll = fimc_m2m_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = fimc_m2m_mmap, -}; - -static struct v4l2_m2m_ops m2m_ops = { - .device_run = fimc_dma_run, - .job_abort = fimc_job_abort, -}; - -int fimc_register_m2m_device(struct fimc_dev *fimc, - struct v4l2_device *v4l2_dev) -{ - struct video_device *vfd; - struct platform_device *pdev; - int ret = 0; - - if (!fimc) - return -ENODEV; - - pdev = fimc->pdev; - fimc->v4l2_dev = v4l2_dev; - - vfd = video_device_alloc(); - if (!vfd) { - v4l2_err(v4l2_dev, "Failed to allocate video device\n"); - return -ENOMEM; - } - - vfd->fops = &fimc_m2m_fops; - vfd->ioctl_ops = &fimc_m2m_ioctl_ops; - vfd->v4l2_dev = v4l2_dev; - vfd->minor = -1; - vfd->release = video_device_release; - vfd->lock = &fimc->lock; - - snprintf(vfd->name, sizeof(vfd->name), "%s.m2m", dev_name(&pdev->dev)); - video_set_drvdata(vfd, fimc); - - fimc->m2m.vfd = vfd; - fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); - if (IS_ERR(fimc->m2m.m2m_dev)) { - v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); - ret = PTR_ERR(fimc->m2m.m2m_dev); - goto err_init; - } - - ret = media_entity_init(&vfd->entity, 0, NULL, 0); - if (!ret) - return 0; - - v4l2_m2m_release(fimc->m2m.m2m_dev); -err_init: - video_device_release(fimc->m2m.vfd); - return ret; -} - -void fimc_unregister_m2m_device(struct fimc_dev *fimc) -{ - if (!fimc) - return; - - if (fimc->m2m.m2m_dev) - v4l2_m2m_release(fimc->m2m.m2m_dev); - if (fimc->m2m.vfd) { - media_entity_cleanup(&fimc->m2m.vfd->entity); - /* Can also be called if video device wasn't registered */ - video_unregister_device(fimc->m2m.vfd); - } -} - static void fimc_clk_put(struct fimc_dev *fimc) { int i; - for (i = 0; i < fimc->num_clocks; i++) { + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { if (IS_ERR_OR_NULL(fimc->clock[i])) continue; clk_unprepare(fimc->clock[i]); @@ -1614,7 +807,7 @@ static int fimc_clk_get(struct fimc_dev *fimc) { int i, ret; - for (i = 0; i < fimc->num_clocks; i++) { + for (i = 0; i < MAX_FIMC_CLOCKS; i++) { fimc->clock[i] = clk_get(&fimc->pdev->dev, fimc_clocks[i]); if (IS_ERR(fimc->clock[i])) goto err; @@ -1672,15 +865,12 @@ static int fimc_m2m_resume(struct fimc_dev *fimc) static int fimc_probe(struct platform_device *pdev) { + struct fimc_drvdata *drv_data = fimc_get_drvdata(pdev); + struct s5p_platform_fimc *pdata; struct fimc_dev *fimc; struct resource *res; - struct samsung_fimc_driverdata *drv_data; - struct s5p_platform_fimc *pdata; int ret = 0; - drv_data = (struct samsung_fimc_driverdata *) - platform_get_device_id(pdev)->driver_data; - if (pdev->id >= drv_data->num_entities) { dev_err(&pdev->dev, "Invalid platform device id: %d\n", pdev->id); @@ -1714,28 +904,29 @@ static int fimc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Failed to get IRQ resource\n"); return -ENXIO; } - fimc->irq = res->start; - fimc->num_clocks = MAX_FIMC_CLOCKS; ret = fimc_clk_get(fimc); if (ret) return ret; clk_set_rate(fimc->clock[CLK_BUS], drv_data->lclk_frequency); clk_enable(fimc->clock[CLK_BUS]); - platform_set_drvdata(pdev, fimc); - - ret = devm_request_irq(&pdev->dev, fimc->irq, fimc_irq_handler, - 0, pdev->name, fimc); + ret = devm_request_irq(&pdev->dev, res->start, fimc_irq_handler, + 0, dev_name(&pdev->dev), fimc); if (ret) { dev_err(&pdev->dev, "failed to install irq (%d)\n", ret); goto err_clk; } + ret = fimc_initialize_capture_subdev(fimc); + if (ret) + goto err_clk; + + platform_set_drvdata(pdev, fimc); pm_runtime_enable(&pdev->dev); ret = pm_runtime_get_sync(&pdev->dev); if (ret < 0) - goto err_clk; + goto err_sd; /* Initialize contiguous memory allocator */ fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(fimc->alloc_ctx)) { @@ -1747,9 +938,10 @@ static int fimc_probe(struct platform_device *pdev) pm_runtime_put(&pdev->dev); return 0; - err_pm: pm_runtime_put(&pdev->dev); +err_sd: + fimc_unregister_capture_subdev(fimc); err_clk: fimc_clk_put(fimc); return ret; @@ -1834,6 +1026,7 @@ static int __devexit fimc_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_set_suspended(&pdev->dev); + fimc_unregister_capture_subdev(fimc); vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); clk_disable(fimc->clock[CLK_BUS]); @@ -1879,7 +1072,7 @@ static struct fimc_pix_limit s5p_pix_limit[4] = { }, }; -static struct samsung_fimc_variant fimc0_variant_s5p = { +static struct fimc_variant fimc0_variant_s5p = { .has_inp_rot = 1, .has_out_rot = 1, .has_cam_if = 1, @@ -1891,17 +1084,17 @@ static struct samsung_fimc_variant fimc0_variant_s5p = { .pix_limit = &s5p_pix_limit[0], }; -static struct samsung_fimc_variant fimc2_variant_s5p = { +static struct fimc_variant fimc2_variant_s5p = { .has_cam_if = 1, .min_inp_pixsize = 16, .min_out_pixsize = 16, .hor_offs_align = 8, .min_vsize_align = 16, .out_buf_count = 4, - .pix_limit = &s5p_pix_limit[1], + .pix_limit = &s5p_pix_limit[1], }; -static struct samsung_fimc_variant fimc0_variant_s5pv210 = { +static struct fimc_variant fimc0_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1914,7 +1107,7 @@ static struct samsung_fimc_variant fimc0_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[1], }; -static struct samsung_fimc_variant fimc1_variant_s5pv210 = { +static struct fimc_variant fimc1_variant_s5pv210 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1928,7 +1121,7 @@ static struct samsung_fimc_variant fimc1_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct samsung_fimc_variant fimc2_variant_s5pv210 = { +static struct fimc_variant fimc2_variant_s5pv210 = { .has_cam_if = 1, .pix_hoff = 1, .min_inp_pixsize = 16, @@ -1939,7 +1132,7 @@ static struct samsung_fimc_variant fimc2_variant_s5pv210 = { .pix_limit = &s5p_pix_limit[2], }; -static struct samsung_fimc_variant fimc0_variant_exynos4 = { +static struct fimc_variant fimc0_variant_exynos4 = { .pix_hoff = 1, .has_inp_rot = 1, .has_out_rot = 1, @@ -1955,7 +1148,7 @@ static struct samsung_fimc_variant fimc0_variant_exynos4 = { .pix_limit = &s5p_pix_limit[1], }; -static struct samsung_fimc_variant fimc3_variant_exynos4 = { +static struct fimc_variant fimc3_variant_exynos4 = { .pix_hoff = 1, .has_cam_if = 1, .has_cistatus2 = 1, @@ -1970,7 +1163,7 @@ static struct samsung_fimc_variant fimc3_variant_exynos4 = { }; /* S5PC100 */ -static struct samsung_fimc_driverdata fimc_drvdata_s5p = { +static struct fimc_drvdata fimc_drvdata_s5p = { .variant = { [0] = &fimc0_variant_s5p, [1] = &fimc0_variant_s5p, @@ -1981,7 +1174,7 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5p = { }; /* S5PV210, S5PC110 */ -static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = { +static struct fimc_drvdata fimc_drvdata_s5pv210 = { .variant = { [0] = &fimc0_variant_s5pv210, [1] = &fimc1_variant_s5pv210, @@ -1991,8 +1184,8 @@ static struct samsung_fimc_driverdata fimc_drvdata_s5pv210 = { .lclk_frequency = 166000000UL, }; -/* S5PV310, S5PC210 */ -static struct samsung_fimc_driverdata fimc_drvdata_exynos4 = { +/* EXYNOS4210, S5PV310, S5PC210 */ +static struct fimc_drvdata fimc_drvdata_exynos4 = { .variant = { [0] = &fimc0_variant_exynos4, [1] = &fimc0_variant_exynos4, @@ -2036,7 +1229,7 @@ static struct platform_driver fimc_driver = { int __init fimc_register_driver(void) { - return platform_driver_probe(&fimc_driver, fimc_probe); + return platform_driver_register(&fimc_driver); } void __exit fimc_unregister_driver(void) diff --git a/drivers/media/video/s5p-fimc/fimc-core.h b/drivers/media/video/s5p-fimc/fimc-core.h index 84fd83550bd7..95b27ae5cf27 100644 --- a/drivers/media/video/s5p-fimc/fimc-core.h +++ b/drivers/media/video/s5p-fimc/fimc-core.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 - 2011 Samsung Electronics Co., Ltd. + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. * * 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 @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -26,8 +27,6 @@ #include #include -#include "regs-fimc.h" - #define err(fmt, args...) \ printk(KERN_ERR "%s:%d: " fmt "\n", __func__, __LINE__, ##args) @@ -78,26 +77,31 @@ enum fimc_dev_flags { #define fimc_capture_busy(dev) test_bit(ST_CAPT_BUSY, &(dev)->state) enum fimc_datapath { - FIMC_CAMERA, - FIMC_DMA, - FIMC_LCDFIFO, - FIMC_WRITEBACK + FIMC_IO_NONE, + FIMC_IO_CAMERA, + FIMC_IO_DMA, + FIMC_IO_LCDFIFO, + FIMC_IO_WRITEBACK, + FIMC_IO_ISP, }; enum fimc_color_fmt { - S5P_FIMC_RGB444 = 0x10, - S5P_FIMC_RGB555, - S5P_FIMC_RGB565, - S5P_FIMC_RGB666, - S5P_FIMC_RGB888, - S5P_FIMC_RGB30_LOCAL, - S5P_FIMC_YCBCR420 = 0x20, - S5P_FIMC_YCBYCR422, - S5P_FIMC_YCRYCB422, - S5P_FIMC_CBYCRY422, - S5P_FIMC_CRYCBY422, - S5P_FIMC_YCBCR444_LOCAL, - S5P_FIMC_JPEG = 0x40, + FIMC_FMT_RGB444 = 0x10, + FIMC_FMT_RGB555, + FIMC_FMT_RGB565, + FIMC_FMT_RGB666, + FIMC_FMT_RGB888, + FIMC_FMT_RGB30_LOCAL, + FIMC_FMT_YCBCR420 = 0x20, + FIMC_FMT_YCBYCR422, + FIMC_FMT_YCRYCB422, + FIMC_FMT_CBYCRY422, + FIMC_FMT_CRYCBY422, + FIMC_FMT_YCBCR444_LOCAL, + FIMC_FMT_JPEG = 0x40, + FIMC_FMT_RAW8 = 0x80, + FIMC_FMT_RAW10, + FIMC_FMT_RAW12, }; #define fimc_fmt_is_rgb(x) (!!((x) & 0x10)) @@ -106,24 +110,11 @@ enum fimc_color_fmt { #define IS_M2M(__strt) ((__strt) == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE || \ __strt == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) -/* Cb/Cr chrominance components order for 2 plane Y/CbCr 4:2:2 formats. */ -#define S5P_FIMC_LSB_CRCB S5P_CIOCTRL_ORDER422_2P_LSB_CRCB - -/* The embedded image effect selection */ -#define S5P_FIMC_EFFECT_ORIGINAL S5P_CIIMGEFF_FIN_BYPASS -#define S5P_FIMC_EFFECT_ARBITRARY S5P_CIIMGEFF_FIN_ARBITRARY -#define S5P_FIMC_EFFECT_NEGATIVE S5P_CIIMGEFF_FIN_NEGATIVE -#define S5P_FIMC_EFFECT_ARTFREEZE S5P_CIIMGEFF_FIN_ARTFREEZE -#define S5P_FIMC_EFFECT_EMBOSSING S5P_CIIMGEFF_FIN_EMBOSSING -#define S5P_FIMC_EFFECT_SIKHOUETTE S5P_CIIMGEFF_FIN_SILHOUETTE - /* The hardware context state. */ #define FIMC_PARAMS (1 << 0) -#define FIMC_SRC_ADDR (1 << 1) -#define FIMC_DST_ADDR (1 << 2) #define FIMC_SRC_FMT (1 << 3) #define FIMC_DST_FMT (1 << 4) -#define FIMC_DST_CROP (1 << 5) +#define FIMC_COMPOSE (1 << 5) #define FIMC_CTX_M2M (1 << 16) #define FIMC_CTX_CAP (1 << 17) #define FIMC_CTX_SHUT (1 << 18) @@ -333,7 +324,7 @@ struct fimc_vid_cap { struct fimc_ctx *ctx; struct vb2_alloc_ctx *alloc_ctx; struct video_device *vfd; - struct v4l2_subdev *subdev; + struct v4l2_subdev subdev; struct media_pad vd_pad; struct v4l2_mbus_framefmt mf; struct media_pad sd_pads[FIMC_SD_PADS_NUM]; @@ -370,8 +361,7 @@ struct fimc_pix_limit { }; /** - * struct samsung_fimc_variant - camera interface variant information - * + * struct fimc_variant - FIMC device variant information * @pix_hoff: indicate whether horizontal offset is in pixels or in bytes * @has_inp_rot: set if has input rotator * @has_out_rot: set if has output rotator @@ -386,7 +376,7 @@ struct fimc_pix_limit { * @min_vsize_align: minimum vertical pixel size alignment * @out_buf_count: the number of buffers in output DMA sequence */ -struct samsung_fimc_variant { +struct fimc_variant { unsigned int pix_hoff:1; unsigned int has_inp_rot:1; unsigned int has_out_rot:1; @@ -403,23 +393,19 @@ struct samsung_fimc_variant { }; /** - * struct samsung_fimc_driverdata - per device type driver data for init time. - * - * @variant: the variant information for this driver. - * @dev_cnt: number of fimc sub-devices available in SoC - * @lclk_frequency: fimc bus clock frequency + * struct fimc_drvdata - per device type driver data + * @variant: variant information for this device + * @num_entities: number of fimc instances available in a SoC + * @lclk_frequency: local bus clock frequency */ -struct samsung_fimc_driverdata { - struct samsung_fimc_variant *variant[FIMC_MAX_DEVS]; - unsigned long lclk_frequency; - int num_entities; +struct fimc_drvdata { + struct fimc_variant *variant[FIMC_MAX_DEVS]; + int num_entities; + unsigned long lclk_frequency; }; -struct fimc_pipeline { - struct media_pipeline *pipe; - struct v4l2_subdev *sensor; - struct v4l2_subdev *csis; -}; +#define fimc_get_drvdata(_pdev) \ + ((struct fimc_drvdata *) platform_get_device_id(_pdev)->driver_data) struct fimc_ctx; @@ -431,10 +417,8 @@ struct fimc_ctx; * @pdata: pointer to the device platform data * @variant: the IP variant information * @id: FIMC device index (0..FIMC_MAX_DEVS) - * @num_clocks: the number of clocks managed by this device instance * @clock: clocks required for FIMC operation * @regs: the mapped hardware registers - * @irq: FIMC interrupt number * @irq_queue: interrupt handler waitqueue * @v4l2_dev: root v4l2_device * @m2m: memory-to-memory V4L2 device information @@ -448,12 +432,10 @@ struct fimc_dev { struct mutex lock; struct platform_device *pdev; struct s5p_platform_fimc *pdata; - struct samsung_fimc_variant *variant; + struct fimc_variant *variant; u16 id; - u16 num_clocks; struct clk *clock[MAX_FIMC_CLOCKS]; void __iomem *regs; - int irq; wait_queue_head_t irq_queue; struct v4l2_device *v4l2_dev; struct fimc_m2m_device m2m; @@ -463,9 +445,32 @@ struct fimc_dev { struct fimc_pipeline pipeline; }; +/** + * struct fimc_ctrls - v4l2 controls structure + * @handler: the control handler + * @colorfx: image effect control + * @colorfx_cbcr: Cb/Cr coefficients control + * @rotate: image rotation control + * @hflip: horizontal flip control + * @vflip: vertical flip control + * @alpha: RGB alpha control + * @ready: true if @handler is initialized + */ +struct fimc_ctrls { + struct v4l2_ctrl_handler handler; + struct { + struct v4l2_ctrl *colorfx; + struct v4l2_ctrl *colorfx_cbcr; + }; + struct v4l2_ctrl *rotate; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *alpha; + bool ready; +}; + /** * fimc_ctx - the device context data - * @slock: spinlock protecting this data structure * @s_frame: source frame properties * @d_frame: destination frame properties * @out_order_1p: output 1-plane YCBCR order @@ -484,15 +489,9 @@ struct fimc_dev { * @fimc_dev: the FIMC device this context applies to * @m2m_ctx: memory-to-memory device context * @fh: v4l2 file handle - * @ctrl_handler: v4l2 controls handler - * @ctrl_rotate image rotation control - * @ctrl_hflip horizontal flip control - * @ctrl_vflip vertical flip control - * @ctrl_alpha RGB alpha control - * @ctrls_rdy: true if the control handler is initialized + * @ctrls: v4l2 controls structure */ struct fimc_ctx { - spinlock_t slock; struct fimc_frame s_frame; struct fimc_frame d_frame; u32 out_order_1p; @@ -511,12 +510,7 @@ struct fimc_ctx { struct fimc_dev *fimc_dev; struct v4l2_m2m_ctx *m2m_ctx; struct v4l2_fh fh; - struct v4l2_ctrl_handler ctrl_handler; - struct v4l2_ctrl *ctrl_rotate; - struct v4l2_ctrl *ctrl_hflip; - struct v4l2_ctrl *ctrl_vflip; - struct v4l2_ctrl *ctrl_alpha; - bool ctrls_rdy; + struct fimc_ctrls ctrls; }; #define fh_to_ctx(__fh) container_of(__fh, struct fimc_ctx, fh) @@ -560,13 +554,13 @@ static inline bool fimc_capture_active(struct fimc_dev *fimc) return ret; } -static inline void fimc_ctx_state_lock_set(u32 state, struct fimc_ctx *ctx) +static inline void fimc_ctx_state_set(u32 state, struct fimc_ctx *ctx) { unsigned long flags; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ctx->state |= state; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); } static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) @@ -574,9 +568,9 @@ static inline bool fimc_ctx_state_is_set(u32 mask, struct fimc_ctx *ctx) unsigned long flags; bool ret; - spin_lock_irqsave(&ctx->slock, flags); + spin_lock_irqsave(&ctx->fimc_dev->slock, flags); ret = (ctx->state & mask) == mask; - spin_unlock_irqrestore(&ctx->slock, flags); + spin_unlock_irqrestore(&ctx->fimc_dev->slock, flags); return ret; } @@ -589,61 +583,13 @@ static inline int tiled_fmt(struct fimc_fmt *fmt) static inline int fimc_get_alpha_mask(struct fimc_fmt *fmt) { switch (fmt->color) { - case S5P_FIMC_RGB444: return 0x0f; - case S5P_FIMC_RGB555: return 0x01; - case S5P_FIMC_RGB888: return 0xff; + case FIMC_FMT_RGB444: return 0x0f; + case FIMC_FMT_RGB555: return 0x01; + case FIMC_FMT_RGB888: return 0xff; default: return 0; }; } -static inline void fimc_hw_clear_irq(struct fimc_dev *dev) -{ - u32 cfg = readl(dev->regs + S5P_CIGCTRL); - cfg |= S5P_CIGCTRL_IRQ_CLR; - writel(cfg, dev->regs + S5P_CIGCTRL); -} - -static inline void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on) -{ - u32 cfg = readl(dev->regs + S5P_CISCCTRL); - if (on) - cfg |= S5P_CISCCTRL_SCALERSTART; - else - cfg &= ~S5P_CISCCTRL_SCALERSTART; - writel(cfg, dev->regs + S5P_CISCCTRL); -} - -static inline void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) -{ - u32 cfg = readl(dev->regs + S5P_MSCTRL); - if (on) - cfg |= S5P_MSCTRL_ENVID; - else - cfg &= ~S5P_MSCTRL_ENVID; - writel(cfg, dev->regs + S5P_MSCTRL); -} - -static inline void fimc_hw_dis_capture(struct fimc_dev *dev) -{ - u32 cfg = readl(dev->regs + S5P_CIIMGCPT); - cfg &= ~(S5P_CIIMGCPT_IMGCPTEN | S5P_CIIMGCPT_IMGCPTEN_SC); - writel(cfg, dev->regs + S5P_CIIMGCPT); -} - -/** - * fimc_hw_set_dma_seq - configure output DMA buffer sequence - * @mask: each bit corresponds to one of 32 output buffer registers set - * 1 to include buffer in the sequence, 0 to disable - * - * This function mask output DMA ring buffers, i.e. it allows to configure - * which of the output buffer address registers will be used by the DMA - * engine. - */ -static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask) -{ - writel(mask, dev->regs + S5P_CIFCNTSEQ); -} - static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, enum v4l2_buf_type type) { @@ -665,48 +611,6 @@ static inline struct fimc_frame *ctx_get_frame(struct fimc_ctx *ctx, return frame; } -/* Return an index to the buffer actually being written. */ -static inline u32 fimc_hw_get_frame_index(struct fimc_dev *dev) -{ - u32 reg; - - if (dev->variant->has_cistatus2) { - reg = readl(dev->regs + S5P_CISTATUS2) & 0x3F; - return reg > 0 ? --reg : reg; - } else { - reg = readl(dev->regs + S5P_CISTATUS); - return (reg & S5P_CISTATUS_FRAMECNT_MASK) >> - S5P_CISTATUS_FRAMECNT_SHIFT; - } -} - -/* -----------------------------------------------------*/ -/* fimc-reg.c */ -void fimc_hw_reset(struct fimc_dev *fimc); -void fimc_hw_set_rotation(struct fimc_ctx *ctx); -void fimc_hw_set_target_format(struct fimc_ctx *ctx); -void fimc_hw_set_out_dma(struct fimc_ctx *ctx); -void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); -void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); -void fimc_hw_set_prescaler(struct fimc_ctx *ctx); -void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); -void fimc_hw_en_capture(struct fimc_ctx *ctx); -void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active); -void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); -void fimc_hw_set_in_dma(struct fimc_ctx *ctx); -void fimc_hw_set_input_path(struct fimc_ctx *ctx); -void fimc_hw_set_output_path(struct fimc_ctx *ctx); -void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); -void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, - int index); -int fimc_hw_set_camera_source(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); -int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); -int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); -int fimc_hw_set_camera_type(struct fimc_dev *fimc, - struct s5p_fimc_isp_info *cam); - /* -----------------------------------------------------*/ /* fimc-core.c */ int fimc_vidioc_enum_fmt_mplane(struct file *file, void *priv, @@ -720,6 +624,7 @@ void fimc_adjust_mplane_format(struct fimc_fmt *fmt, u32 width, u32 height, struct v4l2_pix_format_mplane *pix); struct fimc_fmt *fimc_find_format(const u32 *pixelformat, const u32 *mbus_code, unsigned int mask, int index); +struct fimc_fmt *fimc_get_format(unsigned int index); int fimc_check_scaler_ratio(struct fimc_ctx *ctx, int sw, int sh, int dw, int dh, int rotation); @@ -730,7 +635,7 @@ int fimc_prepare_addr(struct fimc_ctx *ctx, struct vb2_buffer *vb, void fimc_prepare_dma_offset(struct fimc_ctx *ctx, struct fimc_frame *f); void fimc_set_yuv_order(struct fimc_ctx *ctx); void fimc_fill_frame(struct fimc_frame *frame, struct v4l2_format *f); -void fimc_capture_irq_handler(struct fimc_dev *fimc, bool done); +void fimc_capture_irq_handler(struct fimc_dev *fimc, int deq_buf); int fimc_register_m2m_device(struct fimc_dev *fimc, struct v4l2_device *v4l2_dev); @@ -738,34 +643,19 @@ void fimc_unregister_m2m_device(struct fimc_dev *fimc); int fimc_register_driver(void); void fimc_unregister_driver(void); +/* -----------------------------------------------------*/ +/* fimc-m2m.c */ +void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state); + /* -----------------------------------------------------*/ /* fimc-capture.c */ -int fimc_register_capture_device(struct fimc_dev *fimc, - struct v4l2_device *v4l2_dev); -void fimc_unregister_capture_device(struct fimc_dev *fimc); +int fimc_initialize_capture_subdev(struct fimc_dev *fimc); +void fimc_unregister_capture_subdev(struct fimc_dev *fimc); int fimc_capture_ctrls_create(struct fimc_dev *fimc); -int fimc_vid_cap_buf_queue(struct fimc_dev *fimc, - struct fimc_vid_buffer *fimc_vb); void fimc_sensor_notify(struct v4l2_subdev *sd, unsigned int notification, void *arg); int fimc_capture_suspend(struct fimc_dev *fimc); int fimc_capture_resume(struct fimc_dev *fimc); -int fimc_capture_config_update(struct fimc_ctx *ctx); - -/* Locking: the caller holds fimc->slock */ -static inline void fimc_activate_capture(struct fimc_ctx *ctx) -{ - fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); - fimc_hw_en_capture(ctx); -} - -static inline void fimc_deactivate_capture(struct fimc_dev *fimc) -{ - fimc_hw_en_lastirq(fimc, true); - fimc_hw_dis_capture(fimc); - fimc_hw_enable_scaler(fimc, false); - fimc_hw_en_lastirq(fimc, false); -} /* * Buffer list manipulation functions. Must be called with fimc.slock held. diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.c b/drivers/media/video/s5p-fimc/fimc-lite-reg.c new file mode 100644 index 000000000000..419adfb7cdf9 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-lite-reg.c @@ -0,0 +1,300 @@ +/* + * Register interface file for EXYNOS FIMC-LITE (camera interface) driver + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * 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 "fimc-lite-reg.h" +#include "fimc-lite.h" +#include "fimc-core.h" + +#define FLITE_RESET_TIMEOUT 50 /* in ms */ + +void flite_hw_reset(struct fimc_lite *dev) +{ + unsigned long end = jiffies + msecs_to_jiffies(FLITE_RESET_TIMEOUT); + u32 cfg; + + cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + cfg |= FLITE_REG_CIGCTRL_SWRST_REQ; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + + while (time_is_after_jiffies(end)) { + cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + if (cfg & FLITE_REG_CIGCTRL_SWRST_RDY) + break; + usleep_range(1000, 5000); + } + + cfg |= FLITE_REG_CIGCTRL_SWRST; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); +} + +void flite_hw_clear_pending_irq(struct fimc_lite *dev) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS); + cfg &= ~FLITE_REG_CISTATUS_IRQ_CAM; + writel(cfg, dev->regs + FLITE_REG_CISTATUS); +} + +u32 flite_hw_get_interrupt_source(struct fimc_lite *dev) +{ + u32 intsrc = readl(dev->regs + FLITE_REG_CISTATUS); + return intsrc & FLITE_REG_CISTATUS_IRQ_MASK; +} + +void flite_hw_clear_last_capture_end(struct fimc_lite *dev) +{ + + u32 cfg = readl(dev->regs + FLITE_REG_CISTATUS2); + cfg &= ~FLITE_REG_CISTATUS2_LASTCAPEND; + writel(cfg, dev->regs + FLITE_REG_CISTATUS2); +} + +void flite_hw_set_interrupt_mask(struct fimc_lite *dev) +{ + u32 cfg, intsrc; + + /* Select interrupts to be enabled for each output mode */ + if (dev->out_path == FIMC_IO_DMA) { + intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | + FLITE_REG_CIGCTRL_IRQ_LASTEN | + FLITE_REG_CIGCTRL_IRQ_STARTEN; + } else { + /* An output to the FIMC-IS */ + intsrc = FLITE_REG_CIGCTRL_IRQ_OVFEN | + FLITE_REG_CIGCTRL_IRQ_LASTEN; + } + + cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + cfg |= FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK; + cfg &= ~intsrc; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); +} + +void flite_hw_capture_start(struct fimc_lite *dev) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT); + cfg |= FLITE_REG_CIIMGCPT_IMGCPTEN; + writel(cfg, dev->regs + FLITE_REG_CIIMGCPT); +} + +void flite_hw_capture_stop(struct fimc_lite *dev) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIIMGCPT); + cfg &= ~FLITE_REG_CIIMGCPT_IMGCPTEN; + writel(cfg, dev->regs + FLITE_REG_CIIMGCPT); +} + +/* + * Test pattern (color bars) enable/disable. External sensor + * pixel clock must be active for the test pattern to work. + */ +void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + if (on) + cfg |= FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; + else + cfg &= ~FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); +} + +static const u32 src_pixfmt_map[8][3] = { + { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR, + FLITE_REG_CIGCTRL_YUV422_1P }, + { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB, + FLITE_REG_CIGCTRL_YUV422_1P }, + { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY, + FLITE_REG_CIGCTRL_YUV422_1P }, + { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY, + FLITE_REG_CIGCTRL_YUV422_1P }, + { V4L2_PIX_FMT_SGRBG8, 0, FLITE_REG_CIGCTRL_RAW8 }, + { V4L2_PIX_FMT_SGRBG10, 0, FLITE_REG_CIGCTRL_RAW10 }, + { V4L2_PIX_FMT_SGRBG12, 0, FLITE_REG_CIGCTRL_RAW12 }, + { V4L2_MBUS_FMT_JPEG_1X8, 0, FLITE_REG_CIGCTRL_USER(1) }, +}; + +/* Set camera input pixel format and resolution */ +void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f) +{ + enum v4l2_mbus_pixelcode pixelcode = dev->fmt->mbus_code; + unsigned int i = ARRAY_SIZE(src_pixfmt_map); + u32 cfg; + + while (i-- >= 0) { + if (src_pixfmt_map[i][0] == pixelcode) + break; + } + + if (i == 0 && src_pixfmt_map[i][0] != pixelcode) { + v4l2_err(dev->vfd, + "Unsupported pixel code, falling back to %#08x\n", + src_pixfmt_map[i][0]); + } + + cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + cfg &= ~FLITE_REG_CIGCTRL_FMT_MASK; + cfg |= src_pixfmt_map[i][2]; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + + cfg = readl(dev->regs + FLITE_REG_CISRCSIZE); + cfg &= ~(FLITE_REG_CISRCSIZE_ORDER422_MASK | + FLITE_REG_CISRCSIZE_SIZE_CAM_MASK); + cfg |= (f->f_width << 16) | f->f_height; + cfg |= src_pixfmt_map[i][1]; + writel(cfg, dev->regs + FLITE_REG_CISRCSIZE); +} + +/* Set the camera host input window offsets (cropping) */ +void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f) +{ + u32 hoff2, voff2; + u32 cfg; + + cfg = readl(dev->regs + FLITE_REG_CIWDOFST); + cfg &= ~FLITE_REG_CIWDOFST_OFST_MASK; + cfg |= (f->rect.left << 16) | f->rect.top; + cfg |= FLITE_REG_CIWDOFST_WINOFSEN; + writel(cfg, dev->regs + FLITE_REG_CIWDOFST); + + hoff2 = f->f_width - f->rect.width - f->rect.left; + voff2 = f->f_height - f->rect.height - f->rect.top; + + cfg = (hoff2 << 16) | voff2; + writel(cfg, dev->regs + FLITE_REG_CIWDOFST2); +} + +/* Select camera port (A, B) */ +static void flite_hw_set_camera_port(struct fimc_lite *dev, int id) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIGENERAL); + if (id == 0) + cfg &= ~FLITE_REG_CIGENERAL_CAM_B; + else + cfg |= FLITE_REG_CIGENERAL_CAM_B; + writel(cfg, dev->regs + FLITE_REG_CIGENERAL); +} + +/* Select serial or parallel bus, camera port (A,B) and set signals polarity */ +void flite_hw_set_camera_bus(struct fimc_lite *dev, + struct s5p_fimc_isp_info *s_info) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + unsigned int flags = s_info->flags; + + if (s_info->bus_type != FIMC_MIPI_CSI2) { + cfg &= ~(FLITE_REG_CIGCTRL_SELCAM_MIPI | + FLITE_REG_CIGCTRL_INVPOLPCLK | + FLITE_REG_CIGCTRL_INVPOLVSYNC | + FLITE_REG_CIGCTRL_INVPOLHREF); + + if (flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) + cfg |= FLITE_REG_CIGCTRL_INVPOLPCLK; + + if (flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) + cfg |= FLITE_REG_CIGCTRL_INVPOLVSYNC; + + if (flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) + cfg |= FLITE_REG_CIGCTRL_INVPOLHREF; + } else { + cfg |= FLITE_REG_CIGCTRL_SELCAM_MIPI; + } + + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + + flite_hw_set_camera_port(dev, s_info->mux_id); +} + +void flite_hw_set_out_order(struct fimc_lite *dev, struct flite_frame *f) +{ + static const u32 pixcode[4][2] = { + { V4L2_MBUS_FMT_YUYV8_2X8, FLITE_REG_CIODMAFMT_YCBYCR }, + { V4L2_MBUS_FMT_YVYU8_2X8, FLITE_REG_CIODMAFMT_YCRYCB }, + { V4L2_MBUS_FMT_UYVY8_2X8, FLITE_REG_CIODMAFMT_CBYCRY }, + { V4L2_MBUS_FMT_VYUY8_2X8, FLITE_REG_CIODMAFMT_CRYCBY }, + }; + u32 cfg = readl(dev->regs + FLITE_REG_CIODMAFMT); + unsigned int i = ARRAY_SIZE(pixcode); + + while (i-- >= 0) + if (pixcode[i][0] == dev->fmt->mbus_code) + break; + cfg &= ~FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK; + writel(cfg | pixcode[i][1], dev->regs + FLITE_REG_CIODMAFMT); +} + +void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f) +{ + u32 cfg; + + /* Maximum output pixel size */ + cfg = readl(dev->regs + FLITE_REG_CIOCAN); + cfg &= ~FLITE_REG_CIOCAN_MASK; + cfg = (f->f_height << 16) | f->f_width; + writel(cfg, dev->regs + FLITE_REG_CIOCAN); + + /* DMA offsets */ + cfg = readl(dev->regs + FLITE_REG_CIOOFF); + cfg &= ~FLITE_REG_CIOOFF_MASK; + cfg |= (f->rect.top << 16) | f->rect.left; + writel(cfg, dev->regs + FLITE_REG_CIOOFF); +} + +/* Enable/disable output DMA, set output pixel size and offsets (composition) */ +void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, + bool enable) +{ + u32 cfg = readl(dev->regs + FLITE_REG_CIGCTRL); + + if (!enable) { + cfg |= FLITE_REG_CIGCTRL_ODMA_DISABLE; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + return; + } + + cfg &= ~FLITE_REG_CIGCTRL_ODMA_DISABLE; + writel(cfg, dev->regs + FLITE_REG_CIGCTRL); + + flite_hw_set_out_order(dev, f); + flite_hw_set_dma_window(dev, f); +} + +void flite_hw_dump_regs(struct fimc_lite *dev, const char *label) +{ + struct { + u32 offset; + const char * const name; + } registers[] = { + { 0x00, "CISRCSIZE" }, + { 0x04, "CIGCTRL" }, + { 0x08, "CIIMGCPT" }, + { 0x0c, "CICPTSEQ" }, + { 0x10, "CIWDOFST" }, + { 0x14, "CIWDOFST2" }, + { 0x18, "CIODMAFMT" }, + { 0x20, "CIOCAN" }, + { 0x24, "CIOOFF" }, + { 0x30, "CIOSA" }, + { 0x40, "CISTATUS" }, + { 0x44, "CISTATUS2" }, + { 0xf0, "CITHOLD" }, + { 0xfc, "CIGENERAL" }, + }; + u32 i; + + pr_info("--- %s ---\n", label); + for (i = 0; i < ARRAY_SIZE(registers); i++) { + u32 cfg = readl(dev->regs + registers[i].offset); + pr_info("%s: %s:\t0x%08x\n", __func__, registers[i].name, cfg); + } +} diff --git a/drivers/media/video/s5p-fimc/fimc-lite-reg.h b/drivers/media/video/s5p-fimc/fimc-lite-reg.h new file mode 100644 index 000000000000..adb9e9e6f3c2 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-lite-reg.h @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * 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 FIMC_LITE_REG_H_ +#define FIMC_LITE_REG_H_ + +#include "fimc-lite.h" + +/* Camera Source size */ +#define FLITE_REG_CISRCSIZE 0x00 +#define FLITE_REG_CISRCSIZE_ORDER422_IN_YCBYCR (0 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_YCRYCB (1 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_CBYCRY (2 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_IN_CRYCBY (3 << 14) +#define FLITE_REG_CISRCSIZE_ORDER422_MASK (0x3 << 14) +#define FLITE_REG_CISRCSIZE_SIZE_CAM_MASK (0x3fff << 16 | 0x3fff) + +/* Global control */ +#define FLITE_REG_CIGCTRL 0x04 +#define FLITE_REG_CIGCTRL_YUV422_1P (0x1e << 24) +#define FLITE_REG_CIGCTRL_RAW8 (0x2a << 24) +#define FLITE_REG_CIGCTRL_RAW10 (0x2b << 24) +#define FLITE_REG_CIGCTRL_RAW12 (0x2c << 24) +#define FLITE_REG_CIGCTRL_RAW14 (0x2d << 24) +/* User defined formats. x = 0...15 */ +#define FLITE_REG_CIGCTRL_USER(x) ((0x30 + x - 1) << 24) +#define FLITE_REG_CIGCTRL_FMT_MASK (0x3f << 24) +#define FLITE_REG_CIGCTRL_SHADOWMASK_DISABLE (1 << 21) +#define FLITE_REG_CIGCTRL_ODMA_DISABLE (1 << 20) +#define FLITE_REG_CIGCTRL_SWRST_REQ (1 << 19) +#define FLITE_REG_CIGCTRL_SWRST_RDY (1 << 18) +#define FLITE_REG_CIGCTRL_SWRST (1 << 17) +#define FLITE_REG_CIGCTRL_TEST_PATTERN_COLORBAR (1 << 15) +#define FLITE_REG_CIGCTRL_INVPOLPCLK (1 << 14) +#define FLITE_REG_CIGCTRL_INVPOLVSYNC (1 << 13) +#define FLITE_REG_CIGCTRL_INVPOLHREF (1 << 12) +/* Interrupts mask bits (1 disables an interrupt) */ +#define FLITE_REG_CIGCTRL_IRQ_LASTEN (1 << 8) +#define FLITE_REG_CIGCTRL_IRQ_ENDEN (1 << 7) +#define FLITE_REG_CIGCTRL_IRQ_STARTEN (1 << 6) +#define FLITE_REG_CIGCTRL_IRQ_OVFEN (1 << 5) +#define FLITE_REG_CIGCTRL_IRQ_DISABLE_MASK (0xf << 5) +#define FLITE_REG_CIGCTRL_SELCAM_MIPI (1 << 3) + +/* Image Capture Enable */ +#define FLITE_REG_CIIMGCPT 0x08 +#define FLITE_REG_CIIMGCPT_IMGCPTEN (1 << 31) +#define FLITE_REG_CIIMGCPT_CPT_FREN (1 << 25) +#define FLITE_REG_CIIMGCPT_CPT_MOD_FRCNT (1 << 18) +#define FLITE_REG_CIIMGCPT_CPT_MOD_FREN (0 << 18) + +/* Capture Sequence */ +#define FLITE_REG_CICPTSEQ 0x0c + +/* Camera Window Offset */ +#define FLITE_REG_CIWDOFST 0x10 +#define FLITE_REG_CIWDOFST_WINOFSEN (1 << 31) +#define FLITE_REG_CIWDOFST_CLROVIY (1 << 31) +#define FLITE_REG_CIWDOFST_CLROVFICB (1 << 15) +#define FLITE_REG_CIWDOFST_CLROVFICR (1 << 14) +#define FLITE_REG_CIWDOFST_OFST_MASK ((0x1fff << 16) | 0x1fff) + +/* Camera Window Offset2 */ +#define FLITE_REG_CIWDOFST2 0x14 + +/* Camera Output DMA Format */ +#define FLITE_REG_CIODMAFMT 0x18 +#define FLITE_REG_CIODMAFMT_RAW_CON (1 << 15) +#define FLITE_REG_CIODMAFMT_PACK12 (1 << 14) +#define FLITE_REG_CIODMAFMT_CRYCBY (0 << 4) +#define FLITE_REG_CIODMAFMT_CBYCRY (1 << 4) +#define FLITE_REG_CIODMAFMT_YCRYCB (2 << 4) +#define FLITE_REG_CIODMAFMT_YCBYCR (3 << 4) +#define FLITE_REG_CIODMAFMT_YCBCR_ORDER_MASK (0x3 << 4) + +/* Camera Output Canvas */ +#define FLITE_REG_CIOCAN 0x20 +#define FLITE_REG_CIOCAN_MASK ((0x3fff << 16) | 0x3fff) + +/* Camera Output DMA Offset */ +#define FLITE_REG_CIOOFF 0x24 +#define FLITE_REG_CIOOFF_MASK ((0x3fff << 16) | 0x3fff) + +/* Camera Output DMA Start Address */ +#define FLITE_REG_CIOSA 0x30 + +/* Camera Status */ +#define FLITE_REG_CISTATUS 0x40 +#define FLITE_REG_CISTATUS_MIPI_VVALID (1 << 22) +#define FLITE_REG_CISTATUS_MIPI_HVALID (1 << 21) +#define FLITE_REG_CISTATUS_MIPI_DVALID (1 << 20) +#define FLITE_REG_CISTATUS_ITU_VSYNC (1 << 14) +#define FLITE_REG_CISTATUS_ITU_HREFF (1 << 13) +#define FLITE_REG_CISTATUS_OVFIY (1 << 10) +#define FLITE_REG_CISTATUS_OVFICB (1 << 9) +#define FLITE_REG_CISTATUS_OVFICR (1 << 8) +#define FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW (1 << 7) +#define FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND (1 << 6) +#define FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART (1 << 5) +#define FLITE_REG_CISTATUS_IRQ_SRC_FRMEND (1 << 4) +#define FLITE_REG_CISTATUS_IRQ_CAM (1 << 0) +#define FLITE_REG_CISTATUS_IRQ_MASK (0xf << 4) + +/* Camera Status2 */ +#define FLITE_REG_CISTATUS2 0x44 +#define FLITE_REG_CISTATUS2_LASTCAPEND (1 << 1) +#define FLITE_REG_CISTATUS2_FRMEND (1 << 0) + +/* Qos Threshold */ +#define FLITE_REG_CITHOLD 0xf0 +#define FLITE_REG_CITHOLD_W_QOS_EN (1 << 30) + +/* Camera General Purpose */ +#define FLITE_REG_CIGENERAL 0xfc +/* b0: 1 - camera B, 0 - camera A */ +#define FLITE_REG_CIGENERAL_CAM_B (1 << 0) + +/* ---------------------------------------------------------------------------- + * Function declarations + */ +void flite_hw_reset(struct fimc_lite *dev); +void flite_hw_clear_pending_irq(struct fimc_lite *dev); +u32 flite_hw_get_interrupt_source(struct fimc_lite *dev); +void flite_hw_clear_last_capture_end(struct fimc_lite *dev); +void flite_hw_set_interrupt_mask(struct fimc_lite *dev); +void flite_hw_capture_start(struct fimc_lite *dev); +void flite_hw_capture_stop(struct fimc_lite *dev); +void flite_hw_set_camera_bus(struct fimc_lite *dev, + struct s5p_fimc_isp_info *s_info); +void flite_hw_set_camera_polarity(struct fimc_lite *dev, + struct s5p_fimc_isp_info *cam); +void flite_hw_set_window_offset(struct fimc_lite *dev, struct flite_frame *f); +void flite_hw_set_source_format(struct fimc_lite *dev, struct flite_frame *f); + +void flite_hw_set_output_dma(struct fimc_lite *dev, struct flite_frame *f, + bool enable); +void flite_hw_set_dma_window(struct fimc_lite *dev, struct flite_frame *f); +void flite_hw_set_test_pattern(struct fimc_lite *dev, bool on); +void flite_hw_dump_regs(struct fimc_lite *dev, const char *label); + +static inline void flite_hw_set_output_addr(struct fimc_lite *dev, u32 paddr) +{ + writel(paddr, dev->regs + FLITE_REG_CIOSA); +} +#endif /* FIMC_LITE_REG_H */ diff --git a/drivers/media/video/s5p-fimc/fimc-lite.c b/drivers/media/video/s5p-fimc/fimc-lite.c new file mode 100644 index 000000000000..400d701aef04 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-lite.c @@ -0,0 +1,1576 @@ +/* + * Samsung EXYNOS FIMC-LITE (camera host interface) driver +* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki + * + * 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. + */ +#define pr_fmt(fmt) "%s:%d " fmt, __func__, __LINE__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fimc-mdevice.h" +#include "fimc-core.h" +#include "fimc-lite-reg.h" + +static int debug; +module_param(debug, int, 0644); + +static const struct fimc_fmt fimc_lite_formats[] = { + { + .name = "YUV 4:2:2 packed, YCbYCr", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = { 16 }, + .color = FIMC_FMT_YCBYCR422, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_YUYV8_2X8, + }, { + .name = "YUV 4:2:2 packed, CbYCrY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = { 16 }, + .color = FIMC_FMT_CBYCRY422, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_UYVY8_2X8, + }, { + .name = "YUV 4:2:2 packed, CrYCbY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = { 16 }, + .color = FIMC_FMT_CRYCBY422, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_VYUY8_2X8, + }, { + .name = "YUV 4:2:2 packed, YCrYCb", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = { 16 }, + .color = FIMC_FMT_YCRYCB422, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_YVYU8_2X8, + }, { + .name = "RAW8 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG8, + .depth = { 8 }, + .color = FIMC_FMT_RAW8, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG8_1X8, + }, { + .name = "RAW10 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG10, + .depth = { 10 }, + .color = FIMC_FMT_RAW10, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, + }, { + .name = "RAW12 (GRBG)", + .fourcc = V4L2_PIX_FMT_SGRBG12, + .depth = { 12 }, + .color = FIMC_FMT_RAW12, + .memplanes = 1, + .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, + }, +}; + +/** + * fimc_lite_find_format - lookup fimc color format by fourcc or media bus code + * @pixelformat: fourcc to match, ignored if null + * @mbus_code: media bus code to match, ignored if null + * @index: index to the fimc_lite_formats array, ignored if negative + */ +static const struct fimc_fmt *fimc_lite_find_format(const u32 *pixelformat, + const u32 *mbus_code, int index) +{ + const struct fimc_fmt *fmt, *def_fmt = NULL; + unsigned int i; + int id = 0; + + if (index >= (int)ARRAY_SIZE(fimc_lite_formats)) + return NULL; + + for (i = 0; i < ARRAY_SIZE(fimc_lite_formats); ++i) { + fmt = &fimc_lite_formats[i]; + if (pixelformat && fmt->fourcc == *pixelformat) + return fmt; + if (mbus_code && fmt->mbus_code == *mbus_code) + return fmt; + if (index == id) + def_fmt = fmt; + id++; + } + return def_fmt; +} + +static int fimc_lite_hw_init(struct fimc_lite *fimc) +{ + struct fimc_pipeline *pipeline = &fimc->pipeline; + struct fimc_sensor_info *sensor; + unsigned long flags; + + if (pipeline->subdevs[IDX_SENSOR] == NULL) + return -ENXIO; + + if (fimc->fmt == NULL) + return -EINVAL; + + sensor = v4l2_get_subdev_hostdata(pipeline->subdevs[IDX_SENSOR]); + spin_lock_irqsave(&fimc->slock, flags); + + flite_hw_set_camera_bus(fimc, sensor->pdata); + flite_hw_set_source_format(fimc, &fimc->inp_frame); + flite_hw_set_window_offset(fimc, &fimc->inp_frame); + flite_hw_set_output_dma(fimc, &fimc->out_frame, true); + flite_hw_set_interrupt_mask(fimc); + flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); + + if (debug > 0) + flite_hw_dump_regs(fimc, __func__); + + spin_unlock_irqrestore(&fimc->slock, flags); + return 0; +} + +/* + * Reinitialize the driver so it is ready to start the streaming again. + * Set fimc->state to indicate stream off and the hardware shut down state. + * If not suspending (@suspend is false), return any buffers to videobuf2. + * Otherwise put any owned buffers onto the pending buffers queue, so they + * can be re-spun when the device is being resumed. Also perform FIMC + * software reset and disable streaming on the whole pipeline if required. + */ +static int fimc_lite_reinit(struct fimc_lite *fimc, bool suspend) +{ + struct flite_buffer *buf; + unsigned long flags; + bool streaming; + + spin_lock_irqsave(&fimc->slock, flags); + streaming = fimc->state & (1 << ST_SENSOR_STREAM); + + fimc->state &= ~(1 << ST_FLITE_RUN | 1 << ST_FLITE_OFF | + 1 << ST_FLITE_STREAM | 1 << ST_SENSOR_STREAM); + if (suspend) + fimc->state |= (1 << ST_FLITE_SUSPENDED); + else + fimc->state &= ~(1 << ST_FLITE_PENDING | + 1 << ST_FLITE_SUSPENDED); + + /* Release unused buffers */ + while (!suspend && !list_empty(&fimc->pending_buf_q)) { + buf = fimc_lite_pending_queue_pop(fimc); + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + /* If suspending put unused buffers onto pending queue */ + while (!list_empty(&fimc->active_buf_q)) { + buf = fimc_lite_active_queue_pop(fimc); + if (suspend) + fimc_lite_pending_queue_add(fimc, buf); + else + vb2_buffer_done(&buf->vb, VB2_BUF_STATE_ERROR); + } + + spin_unlock_irqrestore(&fimc->slock, flags); + + flite_hw_reset(fimc); + + if (!streaming) + return 0; + + return fimc_pipeline_s_stream(&fimc->pipeline, 0); +} + +static int fimc_lite_stop_capture(struct fimc_lite *fimc, bool suspend) +{ + unsigned long flags; + + if (!fimc_lite_active(fimc)) + return 0; + + spin_lock_irqsave(&fimc->slock, flags); + set_bit(ST_FLITE_OFF, &fimc->state); + flite_hw_capture_stop(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + wait_event_timeout(fimc->irq_queue, + !test_bit(ST_FLITE_OFF, &fimc->state), + (2*HZ/10)); /* 200 ms */ + + return fimc_lite_reinit(fimc, suspend); +} + +/* Must be called with fimc.slock spinlock held. */ +static void fimc_lite_config_update(struct fimc_lite *fimc) +{ + flite_hw_set_window_offset(fimc, &fimc->inp_frame); + flite_hw_set_dma_window(fimc, &fimc->out_frame); + flite_hw_set_test_pattern(fimc, fimc->test_pattern->val); + clear_bit(ST_FLITE_CONFIG, &fimc->state); +} + +static irqreturn_t flite_irq_handler(int irq, void *priv) +{ + struct fimc_lite *fimc = priv; + struct flite_buffer *vbuf; + unsigned long flags; + struct timeval *tv; + struct timespec ts; + u32 intsrc; + + spin_lock_irqsave(&fimc->slock, flags); + + intsrc = flite_hw_get_interrupt_source(fimc); + flite_hw_clear_pending_irq(fimc); + + if (test_and_clear_bit(ST_FLITE_OFF, &fimc->state)) { + wake_up(&fimc->irq_queue); + goto done; + } + + if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_OVERFLOW) { + clear_bit(ST_FLITE_RUN, &fimc->state); + fimc->events.data_overflow++; + } + + if (intsrc & FLITE_REG_CISTATUS_IRQ_SRC_LASTCAPEND) { + flite_hw_clear_last_capture_end(fimc); + clear_bit(ST_FLITE_STREAM, &fimc->state); + wake_up(&fimc->irq_queue); + } + + if (fimc->out_path != FIMC_IO_DMA) + goto done; + + if ((intsrc & FLITE_REG_CISTATUS_IRQ_SRC_FRMSTART) && + test_bit(ST_FLITE_RUN, &fimc->state) && + !list_empty(&fimc->active_buf_q) && + !list_empty(&fimc->pending_buf_q)) { + vbuf = fimc_lite_active_queue_pop(fimc); + ktime_get_ts(&ts); + tv = &vbuf->vb.v4l2_buf.timestamp; + tv->tv_sec = ts.tv_sec; + tv->tv_usec = ts.tv_nsec / NSEC_PER_USEC; + vbuf->vb.v4l2_buf.sequence = fimc->frame_count++; + vb2_buffer_done(&vbuf->vb, VB2_BUF_STATE_DONE); + + vbuf = fimc_lite_pending_queue_pop(fimc); + flite_hw_set_output_addr(fimc, vbuf->paddr); + fimc_lite_active_queue_add(fimc, vbuf); + } + + if (test_bit(ST_FLITE_CONFIG, &fimc->state)) + fimc_lite_config_update(fimc); + + if (list_empty(&fimc->pending_buf_q)) { + flite_hw_capture_stop(fimc); + clear_bit(ST_FLITE_STREAM, &fimc->state); + } +done: + set_bit(ST_FLITE_RUN, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + return IRQ_HANDLED; +} + +static int start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct fimc_lite *fimc = q->drv_priv; + int ret; + + fimc->frame_count = 0; + + ret = fimc_lite_hw_init(fimc); + if (ret) { + fimc_lite_reinit(fimc, false); + return ret; + } + + set_bit(ST_FLITE_PENDING, &fimc->state); + + if (!list_empty(&fimc->active_buf_q) && + !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) { + flite_hw_capture_start(fimc); + + if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) + fimc_pipeline_s_stream(&fimc->pipeline, 1); + } + if (debug > 0) + flite_hw_dump_regs(fimc, __func__); + + return 0; +} + +static int stop_streaming(struct vb2_queue *q) +{ + struct fimc_lite *fimc = q->drv_priv; + + if (!fimc_lite_active(fimc)) + return -EINVAL; + + return fimc_lite_stop_capture(fimc, false); +} + +static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) +{ + const struct v4l2_pix_format_mplane *pixm = NULL; + struct fimc_lite *fimc = vq->drv_priv; + struct flite_frame *frame = &fimc->out_frame; + const struct fimc_fmt *fmt = fimc->fmt; + unsigned long wh; + int i; + + if (pfmt) { + pixm = &pfmt->fmt.pix_mp; + fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, -1); + wh = pixm->width * pixm->height; + } else { + wh = frame->f_width * frame->f_height; + } + + if (fmt == NULL) + return -EINVAL; + + *num_planes = fmt->memplanes; + + for (i = 0; i < fmt->memplanes; i++) { + unsigned int size = (wh * fmt->depth[i]) / 8; + if (pixm) + sizes[i] = max(size, pixm->plane_fmt[i].sizeimage); + else + sizes[i] = size; + allocators[i] = fimc->alloc_ctx; + } + + return 0; +} + +static int buffer_prepare(struct vb2_buffer *vb) +{ + struct vb2_queue *vq = vb->vb2_queue; + struct fimc_lite *fimc = vq->drv_priv; + int i; + + if (fimc->fmt == NULL) + return -EINVAL; + + for (i = 0; i < fimc->fmt->memplanes; i++) { + unsigned long size = fimc->payload[i]; + + if (vb2_plane_size(vb, i) < size) { + v4l2_err(fimc->vfd, + "User buffer too small (%ld < %ld)\n", + vb2_plane_size(vb, i), size); + return -EINVAL; + } + vb2_set_plane_payload(vb, i, size); + } + + return 0; +} + +static void buffer_queue(struct vb2_buffer *vb) +{ + struct flite_buffer *buf + = container_of(vb, struct flite_buffer, vb); + struct fimc_lite *fimc = vb2_get_drv_priv(vb->vb2_queue); + unsigned long flags; + + spin_lock_irqsave(&fimc->slock, flags); + buf->paddr = vb2_dma_contig_plane_dma_addr(vb, 0); + + if (!test_bit(ST_FLITE_SUSPENDED, &fimc->state) && + !test_bit(ST_FLITE_STREAM, &fimc->state) && + list_empty(&fimc->active_buf_q)) { + flite_hw_set_output_addr(fimc, buf->paddr); + fimc_lite_active_queue_add(fimc, buf); + } else { + fimc_lite_pending_queue_add(fimc, buf); + } + + if (vb2_is_streaming(&fimc->vb_queue) && + !list_empty(&fimc->pending_buf_q) && + !test_and_set_bit(ST_FLITE_STREAM, &fimc->state)) { + flite_hw_capture_start(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + if (!test_and_set_bit(ST_SENSOR_STREAM, &fimc->state)) + fimc_pipeline_s_stream(&fimc->pipeline, 1); + return; + } + spin_unlock_irqrestore(&fimc->slock, flags); +} + +static void fimc_lock(struct vb2_queue *vq) +{ + struct fimc_lite *fimc = vb2_get_drv_priv(vq); + mutex_lock(&fimc->lock); +} + +static void fimc_unlock(struct vb2_queue *vq) +{ + struct fimc_lite *fimc = vb2_get_drv_priv(vq); + mutex_unlock(&fimc->lock); +} + +static const struct vb2_ops fimc_lite_qops = { + .queue_setup = queue_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .wait_prepare = fimc_unlock, + .wait_finish = fimc_lock, + .start_streaming = start_streaming, + .stop_streaming = stop_streaming, +}; + +static void fimc_lite_clear_event_counters(struct fimc_lite *fimc) +{ + unsigned long flags; + + spin_lock_irqsave(&fimc->slock, flags); + memset(&fimc->events, 0, sizeof(fimc->events)); + spin_unlock_irqrestore(&fimc->slock, flags); +} + +static int fimc_lite_open(struct file *file) +{ + struct fimc_lite *fimc = video_drvdata(file); + int ret = v4l2_fh_open(file); + + if (ret) + return ret; + + set_bit(ST_FLITE_IN_USE, &fimc->state); + pm_runtime_get_sync(&fimc->pdev->dev); + + if (++fimc->ref_count != 1 || fimc->out_path != FIMC_IO_DMA) + return ret; + + ret = fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd->entity, + true); + if (ret < 0) { + v4l2_err(fimc->vfd, "Video pipeline initialization failed\n"); + pm_runtime_put_sync(&fimc->pdev->dev); + fimc->ref_count--; + v4l2_fh_release(file); + clear_bit(ST_FLITE_IN_USE, &fimc->state); + } + + fimc_lite_clear_event_counters(fimc); + return ret; +} + +static int fimc_lite_close(struct file *file) +{ + struct fimc_lite *fimc = video_drvdata(file); + + if (--fimc->ref_count == 0 && fimc->out_path == FIMC_IO_DMA) { + clear_bit(ST_FLITE_IN_USE, &fimc->state); + fimc_lite_stop_capture(fimc, false); + fimc_pipeline_shutdown(&fimc->pipeline); + clear_bit(ST_FLITE_SUSPENDED, &fimc->state); + } + + pm_runtime_put(&fimc->pdev->dev); + + if (fimc->ref_count == 0) + vb2_queue_release(&fimc->vb_queue); + + return v4l2_fh_release(file); +} + +static unsigned int fimc_lite_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct fimc_lite *fimc = video_drvdata(file); + return vb2_poll(&fimc->vb_queue, file, wait); +} + +static int fimc_lite_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct fimc_lite *fimc = video_drvdata(file); + return vb2_mmap(&fimc->vb_queue, vma); +} + +static const struct v4l2_file_operations fimc_lite_fops = { + .owner = THIS_MODULE, + .open = fimc_lite_open, + .release = fimc_lite_close, + .poll = fimc_lite_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_lite_mmap, +}; + +/* + * Format and crop negotiation helpers + */ + +static const struct fimc_fmt *fimc_lite_try_format(struct fimc_lite *fimc, + u32 *width, u32 *height, + u32 *code, u32 *fourcc, int pad) +{ + struct flite_variant *variant = fimc->variant; + const struct fimc_fmt *fmt; + + fmt = fimc_lite_find_format(fourcc, code, 0); + if (WARN_ON(!fmt)) + return NULL; + + if (code) + *code = fmt->mbus_code; + if (fourcc) + *fourcc = fmt->fourcc; + + if (pad == FLITE_SD_PAD_SINK) { + v4l_bound_align_image(width, 8, variant->max_width, + ffs(variant->out_width_align) - 1, + height, 0, variant->max_height, 0, 0); + } else { + v4l_bound_align_image(width, 8, fimc->inp_frame.rect.width, + ffs(variant->out_width_align) - 1, + height, 0, fimc->inp_frame.rect.height, + 0, 0); + } + + v4l2_dbg(1, debug, &fimc->subdev, "code: 0x%x, %dx%d\n", + code ? *code : 0, *width, *height); + + return fmt; +} + +static void fimc_lite_try_crop(struct fimc_lite *fimc, struct v4l2_rect *r) +{ + struct flite_frame *frame = &fimc->inp_frame; + + v4l_bound_align_image(&r->width, 0, frame->f_width, 0, + &r->height, 0, frame->f_height, 0, 0); + + /* Adjust left/top if cropping rectangle got out of bounds */ + r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); + r->left = round_down(r->left, fimc->variant->win_hor_offs_align); + r->top = clamp_t(u32, r->top, 0, frame->f_height - r->height); + + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, sink fmt: %dx%d", + r->left, r->top, r->width, r->height, + frame->f_width, frame->f_height); +} + +static void fimc_lite_try_compose(struct fimc_lite *fimc, struct v4l2_rect *r) +{ + struct flite_frame *frame = &fimc->out_frame; + struct v4l2_rect *crop_rect = &fimc->inp_frame.rect; + + /* Scaling is not supported so we enforce compose rectangle size + same as size of the sink crop rectangle. */ + r->width = crop_rect->width; + r->height = crop_rect->height; + + /* Adjust left/top if the composing rectangle got out of bounds */ + r->left = clamp_t(u32, r->left, 0, frame->f_width - r->width); + r->left = round_down(r->left, fimc->variant->out_hor_offs_align); + r->top = clamp_t(u32, r->top, 0, fimc->out_frame.f_height - r->height); + + v4l2_dbg(1, debug, &fimc->subdev, "(%d,%d)/%dx%d, source fmt: %dx%d", + r->left, r->top, r->width, r->height, + frame->f_width, frame->f_height); +} + +/* + * Video node ioctl operations + */ +static int fimc_vidioc_querycap_capture(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + strlcpy(cap->driver, FIMC_LITE_DRV_NAME, sizeof(cap->driver)); + cap->bus_info[0] = 0; + cap->card[0] = 0; + cap->capabilities = V4L2_CAP_STREAMING; + return 0; +} + +static int fimc_lite_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + const struct fimc_fmt *fmt; + + if (f->index >= ARRAY_SIZE(fimc_lite_formats)) + return -EINVAL; + + fmt = &fimc_lite_formats[f->index]; + strlcpy(f->description, fmt->name, sizeof(f->description)); + f->pixelformat = fmt->fourcc; + + return 0; +} + +static int fimc_lite_g_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; + struct v4l2_plane_pix_format *plane_fmt = &pixm->plane_fmt[0]; + struct flite_frame *frame = &fimc->out_frame; + const struct fimc_fmt *fmt = fimc->fmt; + + plane_fmt->bytesperline = (frame->f_width * fmt->depth[0]) / 8; + plane_fmt->sizeimage = plane_fmt->bytesperline * frame->f_height; + + pixm->num_planes = fmt->memplanes; + pixm->pixelformat = fmt->fourcc; + pixm->width = frame->f_width; + pixm->height = frame->f_height; + pixm->field = V4L2_FIELD_NONE; + pixm->colorspace = V4L2_COLORSPACE_JPEG; + return 0; +} + +static int fimc_lite_try_fmt(struct fimc_lite *fimc, + struct v4l2_pix_format_mplane *pixm, + const struct fimc_fmt **ffmt) +{ + struct flite_variant *variant = fimc->variant; + u32 bpl = pixm->plane_fmt[0].bytesperline; + const struct fimc_fmt *fmt; + + fmt = fimc_lite_find_format(&pixm->pixelformat, NULL, 0); + if (WARN_ON(fmt == NULL)) + return -EINVAL; + if (ffmt) + *ffmt = fmt; + v4l_bound_align_image(&pixm->width, 8, variant->max_width, + ffs(variant->out_width_align) - 1, + &pixm->height, 0, variant->max_height, 0, 0); + + if ((bpl == 0 || ((bpl * 8) / fmt->depth[0]) < pixm->width)) + pixm->plane_fmt[0].bytesperline = (pixm->width * + fmt->depth[0]) / 8; + + if (pixm->plane_fmt[0].sizeimage == 0) + pixm->plane_fmt[0].sizeimage = (pixm->width * pixm->height * + fmt->depth[0]) / 8; + pixm->num_planes = fmt->memplanes; + pixm->pixelformat = fmt->fourcc; + pixm->colorspace = V4L2_COLORSPACE_JPEG; + pixm->field = V4L2_FIELD_NONE; + return 0; +} + +static int fimc_lite_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, NULL); +} + +static int fimc_lite_s_fmt_mplane(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct v4l2_pix_format_mplane *pixm = &f->fmt.pix_mp; + struct fimc_lite *fimc = video_drvdata(file); + struct flite_frame *frame = &fimc->out_frame; + const struct fimc_fmt *fmt = NULL; + int ret; + + if (vb2_is_busy(&fimc->vb_queue)) + return -EBUSY; + + ret = fimc_lite_try_fmt(fimc, &f->fmt.pix_mp, &fmt); + if (ret < 0) + return ret; + + fimc->fmt = fmt; + fimc->payload[0] = max((pixm->width * pixm->height * fmt->depth[0]) / 8, + pixm->plane_fmt[0].sizeimage); + frame->f_width = pixm->width; + frame->f_height = pixm->height; + + return 0; +} + +static int fimc_pipeline_validate(struct fimc_lite *fimc) +{ + struct v4l2_subdev *sd = &fimc->subdev; + struct v4l2_subdev_format sink_fmt, src_fmt; + struct media_pad *pad; + int ret; + + while (1) { + /* Retrieve format at the sink pad */ + pad = &sd->entity.pads[0]; + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; + /* Don't call FIMC subdev operation to avoid nested locking */ + if (sd == &fimc->subdev) { + struct flite_frame *ff = &fimc->out_frame; + sink_fmt.format.width = ff->f_width; + sink_fmt.format.height = ff->f_height; + sink_fmt.format.code = fimc->fmt->mbus_code; + } else { + sink_fmt.pad = pad->index; + sink_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, + &sink_fmt); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + } + /* Retrieve format at the source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + src_fmt.pad = pad->index; + src_fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + ret = v4l2_subdev_call(sd, pad, get_fmt, NULL, &src_fmt); + if (ret < 0 && ret != -ENOIOCTLCMD) + return -EPIPE; + + if (src_fmt.format.width != sink_fmt.format.width || + src_fmt.format.height != sink_fmt.format.height || + src_fmt.format.code != sink_fmt.format.code) + return -EPIPE; + } + return 0; +} + +static int fimc_lite_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct v4l2_subdev *sensor = fimc->pipeline.subdevs[IDX_SENSOR]; + struct fimc_pipeline *p = &fimc->pipeline; + int ret; + + if (fimc_lite_active(fimc)) + return -EBUSY; + + media_entity_pipeline_start(&sensor->entity, p->m_pipeline); + + ret = fimc_pipeline_validate(fimc); + if (ret) { + media_entity_pipeline_stop(&sensor->entity); + return ret; + } + + return vb2_streamon(&fimc->vb_queue, type); +} + +static int fimc_lite_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct v4l2_subdev *sd = fimc->pipeline.subdevs[IDX_SENSOR]; + int ret; + + ret = vb2_streamoff(&fimc->vb_queue, type); + if (ret == 0) + media_entity_pipeline_stop(&sd->entity); + return ret; +} + +static int fimc_lite_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *reqbufs) +{ + struct fimc_lite *fimc = video_drvdata(file); + int ret; + + reqbufs->count = max_t(u32, FLITE_REQ_BUFS_MIN, reqbufs->count); + ret = vb2_reqbufs(&fimc->vb_queue, reqbufs); + if (!ret < 0) + fimc->reqbufs_count = reqbufs->count; + + return ret; +} + +static int fimc_lite_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_querybuf(&fimc->vb_queue, buf); +} + +static int fimc_lite_qbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_qbuf(&fimc->vb_queue, buf); +} + +static int fimc_lite_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_dqbuf(&fimc->vb_queue, buf, file->f_flags & O_NONBLOCK); +} + +static int fimc_lite_create_bufs(struct file *file, void *priv, + struct v4l2_create_buffers *create) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_create_bufs(&fimc->vb_queue, create); +} + +static int fimc_lite_prepare_buf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct fimc_lite *fimc = video_drvdata(file); + + return vb2_prepare_buf(&fimc->vb_queue, b); +} + +/* Return 1 if rectangle a is enclosed in rectangle b, or 0 otherwise. */ +static int enclosed_rectangle(struct v4l2_rect *a, struct v4l2_rect *b) +{ + if (a->left < b->left || a->top < b->top) + return 0; + if (a->left + a->width > b->left + b->width) + return 0; + if (a->top + a->height > b->top + b->height) + return 0; + + return 1; +} + +static int fimc_lite_g_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct flite_frame *f = &fimc->out_frame; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + return -EINVAL; + + switch (sel->target) { + case V4L2_SEL_TGT_COMPOSE_BOUNDS: + case V4L2_SEL_TGT_COMPOSE_DEFAULT: + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = f->f_width; + sel->r.height = f->f_height; + return 0; + + case V4L2_SEL_TGT_COMPOSE_ACTIVE: + sel->r = f->rect; + return 0; + } + + return -EINVAL; +} + +static int fimc_lite_s_selection(struct file *file, void *fh, + struct v4l2_selection *sel) +{ + struct fimc_lite *fimc = video_drvdata(file); + struct flite_frame *f = &fimc->out_frame; + struct v4l2_rect rect = sel->r; + unsigned long flags; + + if (sel->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE || + sel->target != V4L2_SEL_TGT_COMPOSE_ACTIVE) + return -EINVAL; + + fimc_lite_try_compose(fimc, &rect); + + if ((sel->flags & V4L2_SEL_FLAG_LE) && + !enclosed_rectangle(&rect, &sel->r)) + return -ERANGE; + + if ((sel->flags & V4L2_SEL_FLAG_GE) && + !enclosed_rectangle(&sel->r, &rect)) + return -ERANGE; + + sel->r = rect; + spin_lock_irqsave(&fimc->slock, flags); + f->rect = rect; + set_bit(ST_FLITE_CONFIG, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + + return 0; +} + +static const struct v4l2_ioctl_ops fimc_lite_ioctl_ops = { + .vidioc_querycap = fimc_vidioc_querycap_capture, + .vidioc_enum_fmt_vid_cap_mplane = fimc_lite_enum_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_lite_try_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_lite_s_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_lite_g_fmt_mplane, + .vidioc_g_selection = fimc_lite_g_selection, + .vidioc_s_selection = fimc_lite_s_selection, + .vidioc_reqbufs = fimc_lite_reqbufs, + .vidioc_querybuf = fimc_lite_querybuf, + .vidioc_prepare_buf = fimc_lite_prepare_buf, + .vidioc_create_bufs = fimc_lite_create_bufs, + .vidioc_qbuf = fimc_lite_qbuf, + .vidioc_dqbuf = fimc_lite_dqbuf, + .vidioc_streamon = fimc_lite_streamon, + .vidioc_streamoff = fimc_lite_streamoff, +}; + +/* Capture subdev media entity operations */ +static int fimc_lite_link_setup(struct media_entity *entity, + const struct media_pad *local, + const struct media_pad *remote, u32 flags) +{ + struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity); + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + unsigned int remote_ent_type = media_entity_type(remote->entity); + + if (WARN_ON(fimc == NULL)) + return 0; + + v4l2_dbg(1, debug, sd, "%s: %s --> %s, flags: 0x%x. source_id: 0x%x", + __func__, local->entity->name, remote->entity->name, + flags, fimc->source_subdev_grp_id); + + switch (local->index) { + case FIMC_SD_PAD_SINK: + if (remote_ent_type != MEDIA_ENT_T_V4L2_SUBDEV) + return -EINVAL; + + if (flags & MEDIA_LNK_FL_ENABLED) { + if (fimc->source_subdev_grp_id != 0) + return -EBUSY; + fimc->source_subdev_grp_id = sd->grp_id; + return 0; + } + + fimc->source_subdev_grp_id = 0; + break; + + case FIMC_SD_PAD_SOURCE: + if (!(flags & MEDIA_LNK_FL_ENABLED)) { + fimc->out_path = FIMC_IO_NONE; + return 0; + } + if (remote_ent_type == MEDIA_ENT_T_V4L2_SUBDEV) + fimc->out_path = FIMC_IO_ISP; + else + fimc->out_path = FIMC_IO_DMA; + break; + + default: + v4l2_err(sd, "Invalid pad index\n"); + return -EINVAL; + } + + return 0; +} + +static const struct media_entity_operations fimc_lite_subdev_media_ops = { + .link_setup = fimc_lite_link_setup, +}; + +static int fimc_lite_subdev_enum_mbus_code(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + const struct fimc_fmt *fmt; + + fmt = fimc_lite_find_format(NULL, NULL, code->index); + if (!fmt) + return -EINVAL; + code->code = fmt->mbus_code; + return 0; +} + +static int fimc_lite_subdev_get_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct flite_frame *f = &fimc->out_frame; + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + fmt->format = *mf; + return 0; + } + mf->colorspace = V4L2_COLORSPACE_JPEG; + + mutex_lock(&fimc->lock); + mf->code = fimc->fmt->mbus_code; + + if (fmt->pad == FLITE_SD_PAD_SINK) { + /* full camera input frame size */ + mf->width = f->f_width; + mf->height = f->f_height; + } else { + /* crop size */ + mf->width = f->rect.width; + mf->height = f->rect.height; + } + mutex_unlock(&fimc->lock); + return 0; +} + +static int fimc_lite_subdev_set_fmt(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct v4l2_mbus_framefmt *mf = &fmt->format; + struct flite_frame *sink = &fimc->inp_frame; + const struct fimc_fmt *ffmt; + + v4l2_dbg(1, debug, sd, "pad%d: code: 0x%x, %dx%d", + fmt->pad, mf->code, mf->width, mf->height); + + mf->colorspace = V4L2_COLORSPACE_JPEG; + mutex_lock(&fimc->lock); + + if ((fimc->out_path == FIMC_IO_ISP && sd->entity.stream_count > 0) || + (fimc->out_path == FIMC_IO_DMA && vb2_is_busy(&fimc->vb_queue))) { + mutex_unlock(&fimc->lock); + return -EBUSY; + } + + ffmt = fimc_lite_try_format(fimc, &mf->width, &mf->height, + &mf->code, NULL, fmt->pad); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + mf = v4l2_subdev_get_try_format(fh, fmt->pad); + *mf = fmt->format; + mutex_unlock(&fimc->lock); + return 0; + } + + if (fmt->pad == FLITE_SD_PAD_SINK) { + sink->f_width = mf->width; + sink->f_height = mf->height; + fimc->fmt = ffmt; + /* Set sink crop rectangle */ + sink->rect.width = mf->width; + sink->rect.height = mf->height; + sink->rect.left = 0; + sink->rect.top = 0; + /* Reset source crop rectangle */ + fimc->out_frame.rect = sink->rect; + } else { + /* Allow changing format only on sink pad */ + mf->code = fimc->fmt->mbus_code; + mf->width = sink->rect.width; + mf->height = sink->rect.height; + } + + mutex_unlock(&fimc->lock); + return 0; +} + +static int fimc_lite_subdev_get_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct flite_frame *f = &fimc->inp_frame; + + if ((sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL && + sel->target != V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS) || + sel->pad != FLITE_SD_PAD_SINK) + return -EINVAL; + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + sel->r = *v4l2_subdev_get_try_crop(fh, sel->pad); + return 0; + } + + mutex_lock(&fimc->lock); + if (sel->target == V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL) { + sel->r = f->rect; + } else { + sel->r.left = 0; + sel->r.top = 0; + sel->r.width = f->f_width; + sel->r.height = f->f_height; + } + mutex_unlock(&fimc->lock); + + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d", + __func__, f->rect.left, f->rect.top, f->rect.width, + f->rect.height, f->f_width, f->f_height); + + return 0; +} + +static int fimc_lite_subdev_set_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct flite_frame *f = &fimc->inp_frame; + int ret = 0; + + if (sel->target != V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL || + sel->pad != FLITE_SD_PAD_SINK) + return -EINVAL; + + mutex_lock(&fimc->lock); + fimc_lite_try_crop(fimc, &sel->r); + + if (sel->which == V4L2_SUBDEV_FORMAT_TRY) { + *v4l2_subdev_get_try_crop(fh, sel->pad) = sel->r; + } else { + unsigned long flags; + spin_lock_irqsave(&fimc->slock, flags); + f->rect = sel->r; + /* Same crop rectangle on the source pad */ + fimc->out_frame.rect = sel->r; + set_bit(ST_FLITE_CONFIG, &fimc->state); + spin_unlock_irqrestore(&fimc->slock, flags); + } + mutex_unlock(&fimc->lock); + + v4l2_dbg(1, debug, sd, "%s: (%d,%d) %dx%d, f_w: %d, f_h: %d", + __func__, f->rect.left, f->rect.top, f->rect.width, + f->rect.height, f->f_width, f->f_height); + + return ret; +} + +static int fimc_lite_subdev_s_stream(struct v4l2_subdev *sd, int on) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + + if (fimc->out_path == FIMC_IO_DMA) + return -ENOIOCTLCMD; + + /* TODO: */ + + return 0; +} + +static int fimc_lite_subdev_s_power(struct v4l2_subdev *sd, int on) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + + if (fimc->out_path == FIMC_IO_DMA) + return -ENOIOCTLCMD; + + /* TODO: */ + + return 0; +} + +static int fimc_lite_log_status(struct v4l2_subdev *sd) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + + flite_hw_dump_regs(fimc, __func__); + return 0; +} + +static int fimc_lite_subdev_registered(struct v4l2_subdev *sd) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + struct vb2_queue *q = &fimc->vb_queue; + struct video_device *vfd; + int ret; + + fimc->fmt = &fimc_lite_formats[0]; + fimc->out_path = FIMC_IO_DMA; + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(sd->v4l2_dev, "Failed to allocate video device\n"); + return -ENOMEM; + } + + snprintf(vfd->name, sizeof(vfd->name), "fimc-lite.%d.capture", + fimc->index); + + vfd->fops = &fimc_lite_fops; + vfd->ioctl_ops = &fimc_lite_ioctl_ops; + vfd->v4l2_dev = sd->v4l2_dev; + vfd->minor = -1; + vfd->release = video_device_release; + vfd->lock = &fimc->lock; + fimc->vfd = vfd; + fimc->ref_count = 0; + fimc->reqbufs_count = 0; + + INIT_LIST_HEAD(&fimc->pending_buf_q); + INIT_LIST_HEAD(&fimc->active_buf_q); + + memset(q, 0, sizeof(*q)); + q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + q->io_modes = VB2_MMAP | VB2_USERPTR; + q->ops = &fimc_lite_qops; + q->mem_ops = &vb2_dma_contig_memops; + q->buf_struct_size = sizeof(struct flite_buffer); + q->drv_priv = fimc; + + vb2_queue_init(q); + + fimc->vd_pad.flags = MEDIA_PAD_FL_SINK; + ret = media_entity_init(&vfd->entity, 1, &fimc->vd_pad, 0); + if (ret) + goto err; + + video_set_drvdata(vfd, fimc); + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_vd; + + v4l2_info(sd->v4l2_dev, "Registered %s as /dev/%s\n", + vfd->name, video_device_node_name(vfd)); + return 0; + + err_vd: + media_entity_cleanup(&vfd->entity); + err: + video_device_release(vfd); + return ret; +} + +static void fimc_lite_subdev_unregistered(struct v4l2_subdev *sd) +{ + struct fimc_lite *fimc = v4l2_get_subdevdata(sd); + + if (fimc == NULL) + return; + + if (fimc->vfd) { + video_unregister_device(fimc->vfd); + media_entity_cleanup(&fimc->vfd->entity); + fimc->vfd = NULL; + } +} + +static const struct v4l2_subdev_internal_ops fimc_lite_subdev_internal_ops = { + .registered = fimc_lite_subdev_registered, + .unregistered = fimc_lite_subdev_unregistered, +}; + +static const struct v4l2_subdev_pad_ops fimc_lite_subdev_pad_ops = { + .enum_mbus_code = fimc_lite_subdev_enum_mbus_code, + .get_selection = fimc_lite_subdev_get_selection, + .set_selection = fimc_lite_subdev_set_selection, + .get_fmt = fimc_lite_subdev_get_fmt, + .set_fmt = fimc_lite_subdev_set_fmt, +}; + +static const struct v4l2_subdev_video_ops fimc_lite_subdev_video_ops = { + .s_stream = fimc_lite_subdev_s_stream, +}; + +static const struct v4l2_subdev_core_ops fimc_lite_core_ops = { + .s_power = fimc_lite_subdev_s_power, + .log_status = fimc_lite_log_status, +}; + +static struct v4l2_subdev_ops fimc_lite_subdev_ops = { + .core = &fimc_lite_core_ops, + .video = &fimc_lite_subdev_video_ops, + .pad = &fimc_lite_subdev_pad_ops, +}; + +static int fimc_lite_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct fimc_lite *fimc = container_of(ctrl->handler, struct fimc_lite, + ctrl_handler); + set_bit(ST_FLITE_CONFIG, &fimc->state); + return 0; +} + +static const struct v4l2_ctrl_ops fimc_lite_ctrl_ops = { + .s_ctrl = fimc_lite_s_ctrl, +}; + +static const struct v4l2_ctrl_config fimc_lite_ctrl = { + .ops = &fimc_lite_ctrl_ops, + .id = V4L2_CTRL_CLASS_USER | 0x1001, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Test Pattern 640x480", +}; + +static int fimc_lite_create_capture_subdev(struct fimc_lite *fimc) +{ + struct v4l2_ctrl_handler *handler = &fimc->ctrl_handler; + struct v4l2_subdev *sd = &fimc->subdev; + int ret; + + v4l2_subdev_init(sd, &fimc_lite_subdev_ops); + sd->flags = V4L2_SUBDEV_FL_HAS_DEVNODE; + snprintf(sd->name, sizeof(sd->name), "FIMC-LITE.%d", fimc->index); + + fimc->subdev_pads[FIMC_SD_PAD_SINK].flags = MEDIA_PAD_FL_SINK; + fimc->subdev_pads[FIMC_SD_PAD_SOURCE].flags = MEDIA_PAD_FL_SOURCE; + ret = media_entity_init(&sd->entity, FIMC_SD_PADS_NUM, + fimc->subdev_pads, 0); + if (ret) + return ret; + + v4l2_ctrl_handler_init(handler, 1); + fimc->test_pattern = v4l2_ctrl_new_custom(handler, &fimc_lite_ctrl, + NULL); + if (handler->error) { + media_entity_cleanup(&sd->entity); + return handler->error; + } + + sd->ctrl_handler = handler; + sd->internal_ops = &fimc_lite_subdev_internal_ops; + sd->entity.ops = &fimc_lite_subdev_media_ops; + v4l2_set_subdevdata(sd, fimc); + + return 0; +} + +static void fimc_lite_unregister_capture_subdev(struct fimc_lite *fimc) +{ + struct v4l2_subdev *sd = &fimc->subdev; + + v4l2_device_unregister_subdev(sd); + media_entity_cleanup(&sd->entity); + v4l2_ctrl_handler_free(&fimc->ctrl_handler); + v4l2_set_subdevdata(sd, NULL); +} + +static void fimc_lite_clk_put(struct fimc_lite *fimc) +{ + if (IS_ERR_OR_NULL(fimc->clock)) + return; + + clk_unprepare(fimc->clock); + clk_put(fimc->clock); + fimc->clock = NULL; +} + +static int fimc_lite_clk_get(struct fimc_lite *fimc) +{ + int ret; + + fimc->clock = clk_get(&fimc->pdev->dev, FLITE_CLK_NAME); + if (IS_ERR(fimc->clock)) + return PTR_ERR(fimc->clock); + + ret = clk_prepare(fimc->clock); + if (ret < 0) { + clk_put(fimc->clock); + fimc->clock = NULL; + } + return ret; +} + +static int __devinit fimc_lite_probe(struct platform_device *pdev) +{ + struct flite_drvdata *drv_data = fimc_lite_get_drvdata(pdev); + struct fimc_lite *fimc; + struct resource *res; + int ret; + + fimc = devm_kzalloc(&pdev->dev, sizeof(*fimc), GFP_KERNEL); + if (!fimc) + return -ENOMEM; + + fimc->index = pdev->id; + fimc->variant = drv_data->variant[fimc->index]; + fimc->pdev = pdev; + + init_waitqueue_head(&fimc->irq_queue); + spin_lock_init(&fimc->slock); + mutex_init(&fimc->lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + fimc->regs = devm_request_and_ioremap(&pdev->dev, res); + if (fimc->regs == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; + } + + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (res == NULL) { + dev_err(&pdev->dev, "Failed to get IRQ resource\n"); + return -ENXIO; + } + + ret = fimc_lite_clk_get(fimc); + if (ret) + return ret; + + ret = devm_request_irq(&pdev->dev, res->start, flite_irq_handler, + 0, dev_name(&pdev->dev), fimc); + if (ret) { + dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); + goto err_clk; + } + + /* The video node will be created within the subdev's registered() op */ + ret = fimc_lite_create_capture_subdev(fimc); + if (ret) + goto err_clk; + + platform_set_drvdata(pdev, fimc); + pm_runtime_enable(&pdev->dev); + ret = pm_runtime_get_sync(&pdev->dev); + if (ret < 0) + goto err_sd; + + fimc->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); + if (IS_ERR(fimc->alloc_ctx)) { + ret = PTR_ERR(fimc->alloc_ctx); + goto err_pm; + } + pm_runtime_put(&pdev->dev); + + dev_dbg(&pdev->dev, "FIMC-LITE.%d registered successfully\n", + fimc->index); + return 0; +err_pm: + pm_runtime_put(&pdev->dev); +err_sd: + fimc_lite_unregister_capture_subdev(fimc); +err_clk: + fimc_lite_clk_put(fimc); + return ret; +} + +static int fimc_lite_runtime_resume(struct device *dev) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + + clk_enable(fimc->clock); + return 0; +} + +static int fimc_lite_runtime_suspend(struct device *dev) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + + clk_disable(fimc->clock); + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int fimc_lite_resume(struct device *dev) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + struct flite_buffer *buf; + unsigned long flags; + int i; + + spin_lock_irqsave(&fimc->slock, flags); + if (!test_and_clear_bit(ST_LPM, &fimc->state) || + !test_bit(ST_FLITE_IN_USE, &fimc->state)) { + spin_unlock_irqrestore(&fimc->slock, flags); + return 0; + } + flite_hw_reset(fimc); + spin_unlock_irqrestore(&fimc->slock, flags); + + if (!test_and_clear_bit(ST_FLITE_SUSPENDED, &fimc->state)) + return 0; + + INIT_LIST_HEAD(&fimc->active_buf_q); + fimc_pipeline_initialize(&fimc->pipeline, &fimc->vfd->entity, false); + fimc_lite_hw_init(fimc); + clear_bit(ST_FLITE_SUSPENDED, &fimc->state); + + for (i = 0; i < fimc->reqbufs_count; i++) { + if (list_empty(&fimc->pending_buf_q)) + break; + buf = fimc_lite_pending_queue_pop(fimc); + buffer_queue(&buf->vb); + } + return 0; +} + +static int fimc_lite_suspend(struct device *dev) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + bool suspend = test_bit(ST_FLITE_IN_USE, &fimc->state); + int ret; + + if (test_and_set_bit(ST_LPM, &fimc->state)) + return 0; + + ret = fimc_lite_stop_capture(fimc, suspend); + if (ret) + return ret; + + return fimc_pipeline_shutdown(&fimc->pipeline); +} +#endif /* CONFIG_PM_SLEEP */ + +static int __devexit fimc_lite_remove(struct platform_device *pdev) +{ + struct fimc_lite *fimc = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + fimc_lite_unregister_capture_subdev(fimc); + vb2_dma_contig_cleanup_ctx(fimc->alloc_ctx); + fimc_lite_clk_put(fimc); + + dev_info(dev, "Driver unloaded\n"); + return 0; +} + +static struct flite_variant fimc_lite0_variant_exynos4 = { + .max_width = 8192, + .max_height = 8192, + .out_width_align = 8, + .win_hor_offs_align = 2, + .out_hor_offs_align = 8, +}; + +/* EXYNOS4212, EXYNOS4412 */ +static struct flite_drvdata fimc_lite_drvdata_exynos4 = { + .variant = { + [0] = &fimc_lite0_variant_exynos4, + [1] = &fimc_lite0_variant_exynos4, + }, +}; + +static struct platform_device_id fimc_lite_driver_ids[] = { + { + .name = "exynos-fimc-lite", + .driver_data = (unsigned long)&fimc_lite_drvdata_exynos4, + }, + { /* sentinel */ }, +}; +MODULE_DEVICE_TABLE(platform, fimc_lite_driver_ids); + +static const struct dev_pm_ops fimc_lite_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fimc_lite_suspend, fimc_lite_resume) + SET_RUNTIME_PM_OPS(fimc_lite_runtime_suspend, fimc_lite_runtime_resume, + NULL) +}; + +static struct platform_driver fimc_lite_driver = { + .probe = fimc_lite_probe, + .remove = __devexit_p(fimc_lite_remove), + .id_table = fimc_lite_driver_ids, + .driver = { + .name = FIMC_LITE_DRV_NAME, + .owner = THIS_MODULE, + .pm = &fimc_lite_pm_ops, + } +}; +module_platform_driver(fimc_lite_driver); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" FIMC_LITE_DRV_NAME); diff --git a/drivers/media/video/s5p-fimc/fimc-lite.h b/drivers/media/video/s5p-fimc/fimc-lite.h new file mode 100644 index 000000000000..44424eee81d8 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-lite.h @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * 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 FIMC_LITE_H_ +#define FIMC_LITE_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "fimc-core.h" + +#define FIMC_LITE_DRV_NAME "exynos-fimc-lite" +#define FLITE_CLK_NAME "flite" +#define FIMC_LITE_MAX_DEVS 2 +#define FLITE_REQ_BUFS_MIN 2 + +/* Bit index definitions for struct fimc_lite::state */ +enum { + ST_FLITE_LPM, + ST_FLITE_PENDING, + ST_FLITE_RUN, + ST_FLITE_STREAM, + ST_FLITE_SUSPENDED, + ST_FLITE_OFF, + ST_FLITE_IN_USE, + ST_FLITE_CONFIG, + ST_SENSOR_STREAM, +}; + +#define FLITE_SD_PAD_SINK 0 +#define FLITE_SD_PAD_SOURCE 1 +#define FLITE_SD_PADS_NUM 2 + +struct flite_variant { + unsigned short max_width; + unsigned short max_height; + unsigned short out_width_align; + unsigned short win_hor_offs_align; + unsigned short out_hor_offs_align; +}; + +struct flite_drvdata { + struct flite_variant *variant[FIMC_LITE_MAX_DEVS]; +}; + +#define fimc_lite_get_drvdata(_pdev) \ + ((struct flite_drvdata *) platform_get_device_id(_pdev)->driver_data) + +struct fimc_lite_events { + unsigned int data_overflow; +}; + +#define FLITE_MAX_PLANES 1 + +/** + * struct flite_frame - source/target frame properties + * @f_width: full pixel width + * @f_height: full pixel height + * @rect: crop/composition rectangle + */ +struct flite_frame { + u16 f_width; + u16 f_height; + struct v4l2_rect rect; +}; + +/** + * struct flite_buffer - video buffer structure + * @vb: vb2 buffer + * @list: list head for the buffers queue + * @paddr: precalculated physical address + */ +struct flite_buffer { + struct vb2_buffer vb; + struct list_head list; + dma_addr_t paddr; +}; + +/** + * struct fimc_lite - fimc lite structure + * @pdev: pointer to FIMC-LITE platform device + * @variant: variant information for this IP + * @v4l2_dev: pointer to top the level v4l2_device + * @vfd: video device node + * @fh: v4l2 file handle + * @alloc_ctx: videobuf2 memory allocator context + * @subdev: FIMC-LITE subdev + * @vd_pad: media (sink) pad for the capture video node + * @subdev_pads: the subdev media pads + * @ctrl_handler: v4l2 control handler + * @test_pattern: test pattern controls + * @index: FIMC-LITE platform device index + * @pipeline: video capture pipeline data structure + * @slock: spinlock protecting this data structure and the hw registers + * @lock: mutex serializing video device and the subdev operations + * @clock: FIMC-LITE gate clock + * @regs: memory mapped io registers + * @irq_queue: interrupt handler waitqueue + * @fmt: pointer to color format description structure + * @payload: image size in bytes (w x h x bpp) + * @inp_frame: camera input frame structure + * @out_frame: DMA output frame structure + * @out_path: output data path (DMA or FIFO) + * @source_subdev_grp_id: source subdev group id + * @state: driver state flags + * @pending_buf_q: pending buffers queue head + * @active_buf_q: the queue head of buffers scheduled in hardware + * @vb_queue: vb2 buffers queue + * @active_buf_count: number of video buffers scheduled in hardware + * @frame_count: the captured frames counter + * @reqbufs_count: the number of buffers requested with REQBUFS ioctl + * @ref_count: driver's private reference counter + */ +struct fimc_lite { + struct platform_device *pdev; + struct flite_variant *variant; + struct v4l2_device *v4l2_dev; + struct video_device *vfd; + struct v4l2_fh fh; + struct vb2_alloc_ctx *alloc_ctx; + struct v4l2_subdev subdev; + struct media_pad vd_pad; + struct media_pad subdev_pads[FLITE_SD_PADS_NUM]; + struct v4l2_ctrl_handler ctrl_handler; + struct v4l2_ctrl *test_pattern; + u32 index; + struct fimc_pipeline pipeline; + + struct mutex lock; + spinlock_t slock; + + struct clk *clock; + void __iomem *regs; + wait_queue_head_t irq_queue; + + const struct fimc_fmt *fmt; + unsigned long payload[FLITE_MAX_PLANES]; + struct flite_frame inp_frame; + struct flite_frame out_frame; + enum fimc_datapath out_path; + unsigned int source_subdev_grp_id; + + unsigned long state; + struct list_head pending_buf_q; + struct list_head active_buf_q; + struct vb2_queue vb_queue; + unsigned int frame_count; + unsigned int reqbufs_count; + int ref_count; + + struct fimc_lite_events events; +}; + +static inline bool fimc_lite_active(struct fimc_lite *fimc) +{ + unsigned long flags; + bool ret; + + spin_lock_irqsave(&fimc->slock, flags); + ret = fimc->state & (1 << ST_FLITE_RUN) || + fimc->state & (1 << ST_FLITE_PENDING); + spin_unlock_irqrestore(&fimc->slock, flags); + return ret; +} + +static inline void fimc_lite_active_queue_add(struct fimc_lite *dev, + struct flite_buffer *buf) +{ + list_add_tail(&buf->list, &dev->active_buf_q); +} + +static inline struct flite_buffer *fimc_lite_active_queue_pop( + struct fimc_lite *dev) +{ + struct flite_buffer *buf = list_entry(dev->active_buf_q.next, + struct flite_buffer, list); + list_del(&buf->list); + return buf; +} + +static inline void fimc_lite_pending_queue_add(struct fimc_lite *dev, + struct flite_buffer *buf) +{ + list_add_tail(&buf->list, &dev->pending_buf_q); +} + +static inline struct flite_buffer *fimc_lite_pending_queue_pop( + struct fimc_lite *dev) +{ + struct flite_buffer *buf = list_entry(dev->pending_buf_q.next, + struct flite_buffer, list); + list_del(&buf->list); + return buf; +} + +#endif /* FIMC_LITE_H_ */ diff --git a/drivers/media/video/s5p-fimc/fimc-m2m.c b/drivers/media/video/s5p-fimc/fimc-m2m.c new file mode 100644 index 000000000000..4c58e0570962 --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-m2m.c @@ -0,0 +1,824 @@ +/* + * Samsung S5P/EXYNOS4 SoC series FIMC (video postprocessor) driver + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki, + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "fimc-core.h" +#include "fimc-reg.h" +#include "fimc-mdevice.h" + + +static unsigned int get_m2m_fmt_flags(unsigned int stream_type) +{ + if (stream_type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + return FMT_FLAGS_M2M_IN; + else + return FMT_FLAGS_M2M_OUT; +} + +void fimc_m2m_job_finish(struct fimc_ctx *ctx, int vb_state) +{ + struct vb2_buffer *src_vb, *dst_vb; + + if (!ctx || !ctx->m2m_ctx) + return; + + src_vb = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); + dst_vb = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); + + if (src_vb && dst_vb) { + v4l2_m2m_buf_done(src_vb, vb_state); + v4l2_m2m_buf_done(dst_vb, vb_state); + v4l2_m2m_job_finish(ctx->fimc_dev->m2m.m2m_dev, + ctx->m2m_ctx); + } +} + +/* Complete the transaction which has been scheduled for execution. */ +static int fimc_m2m_shutdown(struct fimc_ctx *ctx) +{ + struct fimc_dev *fimc = ctx->fimc_dev; + int ret; + + if (!fimc_m2m_pending(fimc)) + return 0; + + fimc_ctx_state_set(FIMC_CTX_SHUT, ctx); + + ret = wait_event_timeout(fimc->irq_queue, + !fimc_ctx_state_is_set(FIMC_CTX_SHUT, ctx), + FIMC_SHUTDOWN_TIMEOUT); + + return ret == 0 ? -ETIMEDOUT : ret; +} + +static int start_streaming(struct vb2_queue *q, unsigned int count) +{ + struct fimc_ctx *ctx = q->drv_priv; + int ret; + + ret = pm_runtime_get_sync(&ctx->fimc_dev->pdev->dev); + return ret > 0 ? 0 : ret; +} + +static int stop_streaming(struct vb2_queue *q) +{ + struct fimc_ctx *ctx = q->drv_priv; + int ret; + + ret = fimc_m2m_shutdown(ctx); + if (ret == -ETIMEDOUT) + fimc_m2m_job_finish(ctx, VB2_BUF_STATE_ERROR); + + pm_runtime_put(&ctx->fimc_dev->pdev->dev); + return 0; +} + +static void fimc_device_run(void *priv) +{ + struct vb2_buffer *vb = NULL; + struct fimc_ctx *ctx = priv; + struct fimc_frame *sf, *df; + struct fimc_dev *fimc; + unsigned long flags; + u32 ret; + + if (WARN(!ctx, "Null context\n")) + return; + + fimc = ctx->fimc_dev; + spin_lock_irqsave(&fimc->slock, flags); + + set_bit(ST_M2M_PEND, &fimc->state); + sf = &ctx->s_frame; + df = &ctx->d_frame; + + if (ctx->state & FIMC_PARAMS) { + /* Prepare the DMA offsets for scaler */ + fimc_prepare_dma_offset(ctx, sf); + fimc_prepare_dma_offset(ctx, df); + } + + vb = v4l2_m2m_next_src_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, sf, &sf->paddr); + if (ret) + goto dma_unlock; + + vb = v4l2_m2m_next_dst_buf(ctx->m2m_ctx); + ret = fimc_prepare_addr(ctx, vb, df, &df->paddr); + if (ret) + goto dma_unlock; + + /* Reconfigure hardware if the context has changed. */ + if (fimc->m2m.ctx != ctx) { + ctx->state |= FIMC_PARAMS; + fimc->m2m.ctx = ctx; + } + + if (ctx->state & FIMC_PARAMS) { + fimc_set_yuv_order(ctx); + fimc_hw_set_input_path(ctx); + fimc_hw_set_in_dma(ctx); + ret = fimc_set_scaler_info(ctx); + if (ret) + goto dma_unlock; + fimc_hw_set_prescaler(ctx); + fimc_hw_set_mainscaler(ctx); + fimc_hw_set_target_format(ctx); + fimc_hw_set_rotation(ctx); + fimc_hw_set_effect(ctx); + fimc_hw_set_out_dma(ctx); + if (fimc->variant->has_alpha) + fimc_hw_set_rgb_alpha(ctx); + fimc_hw_set_output_path(ctx); + } + fimc_hw_set_input_addr(fimc, &sf->paddr); + fimc_hw_set_output_addr(fimc, &df->paddr, -1); + + fimc_activate_capture(ctx); + ctx->state &= (FIMC_CTX_M2M | FIMC_CTX_CAP | + FIMC_SRC_FMT | FIMC_DST_FMT); + fimc_hw_activate_input_dma(fimc, true); + +dma_unlock: + spin_unlock_irqrestore(&fimc->slock, flags); +} + +static void fimc_job_abort(void *priv) +{ + fimc_m2m_shutdown(priv); +} + +static int fimc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, + unsigned int *num_buffers, unsigned int *num_planes, + unsigned int sizes[], void *allocators[]) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + struct fimc_frame *f; + int i; + + f = ctx_get_frame(ctx, vq->type); + if (IS_ERR(f)) + return PTR_ERR(f); + /* + * Return number of non-contigous planes (plane buffers) + * depending on the configured color format. + */ + if (!f->fmt) + return -EINVAL; + + *num_planes = f->fmt->memplanes; + for (i = 0; i < f->fmt->memplanes; i++) { + sizes[i] = (f->f_width * f->f_height * f->fmt->depth[i]) / 8; + allocators[i] = ctx->fimc_dev->alloc_ctx; + } + return 0; +} + +static int fimc_buf_prepare(struct vb2_buffer *vb) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + struct fimc_frame *frame; + int i; + + frame = ctx_get_frame(ctx, vb->vb2_queue->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + for (i = 0; i < frame->fmt->memplanes; i++) + vb2_set_plane_payload(vb, i, frame->payload[i]); + + return 0; +} + +static void fimc_buf_queue(struct vb2_buffer *vb) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue); + + dbg("ctx: %p, ctx->state: 0x%x", ctx, ctx->state); + + if (ctx->m2m_ctx) + v4l2_m2m_buf_queue(ctx->m2m_ctx, vb); +} + +static void fimc_lock(struct vb2_queue *vq) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + mutex_lock(&ctx->fimc_dev->lock); +} + +static void fimc_unlock(struct vb2_queue *vq) +{ + struct fimc_ctx *ctx = vb2_get_drv_priv(vq); + mutex_unlock(&ctx->fimc_dev->lock); +} + +static struct vb2_ops fimc_qops = { + .queue_setup = fimc_queue_setup, + .buf_prepare = fimc_buf_prepare, + .buf_queue = fimc_buf_queue, + .wait_prepare = fimc_unlock, + .wait_finish = fimc_lock, + .stop_streaming = stop_streaming, + .start_streaming = start_streaming, +}; + +/* + * V4L2 ioctl handlers + */ +static int fimc_m2m_querycap(struct file *file, void *fh, + struct v4l2_capability *cap) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_dev *fimc = ctx->fimc_dev; + + strncpy(cap->driver, fimc->pdev->name, sizeof(cap->driver) - 1); + strncpy(cap->card, fimc->pdev->name, sizeof(cap->card) - 1); + cap->bus_info[0] = 0; + cap->capabilities = V4L2_CAP_STREAMING | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE; + + return 0; +} + +static int fimc_m2m_enum_fmt_mplane(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + struct fimc_fmt *fmt; + + fmt = fimc_find_format(NULL, NULL, get_m2m_fmt_flags(f->type), + f->index); + if (!fmt) + return -EINVAL; + + strncpy(f->description, fmt->name, sizeof(f->description) - 1); + f->pixelformat = fmt->fourcc; + return 0; +} + +static int fimc_m2m_g_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_frame *frame = ctx_get_frame(ctx, f->type); + + if (IS_ERR(frame)) + return PTR_ERR(frame); + + return fimc_fill_format(frame, f); +} + +static int fimc_try_fmt_mplane(struct fimc_ctx *ctx, struct v4l2_format *f) +{ + struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_variant *variant = fimc->variant; + struct v4l2_pix_format_mplane *pix = &f->fmt.pix_mp; + struct fimc_fmt *fmt; + u32 max_w, mod_x, mod_y; + + if (!IS_M2M(f->type)) + return -EINVAL; + + dbg("w: %d, h: %d", pix->width, pix->height); + + fmt = fimc_find_format(&pix->pixelformat, NULL, + get_m2m_fmt_flags(f->type), 0); + if (WARN(fmt == NULL, "Pixel format lookup failed")) + return -EINVAL; + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_NONE; + else if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + max_w = variant->pix_limit->scaler_dis_w; + mod_x = ffs(variant->min_inp_pixsize) - 1; + } else { + max_w = variant->pix_limit->out_rot_dis_w; + mod_x = ffs(variant->min_out_pixsize) - 1; + } + + if (tiled_fmt(fmt)) { + mod_x = 6; /* 64 x 32 pixels tile */ + mod_y = 5; + } else { + if (variant->min_vsize_align == 1) + mod_y = fimc_fmt_is_rgb(fmt->color) ? 0 : 1; + else + mod_y = ffs(variant->min_vsize_align) - 1; + } + + v4l_bound_align_image(&pix->width, 16, max_w, mod_x, + &pix->height, 8, variant->pix_limit->scaler_dis_w, mod_y, 0); + + fimc_adjust_mplane_format(fmt, pix->width, pix->height, &f->fmt.pix_mp); + return 0; +} + +static int fimc_m2m_try_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return fimc_try_fmt_mplane(ctx, f); +} + +static int fimc_m2m_s_fmt_mplane(struct file *file, void *fh, + struct v4l2_format *f) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_dev *fimc = ctx->fimc_dev; + struct vb2_queue *vq; + struct fimc_frame *frame; + struct v4l2_pix_format_mplane *pix; + int i, ret = 0; + + ret = fimc_try_fmt_mplane(ctx, f); + if (ret) + return ret; + + vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type); + + if (vb2_is_busy(vq)) { + v4l2_err(fimc->m2m.vfd, "queue (%d) busy\n", f->type); + return -EBUSY; + } + + if (f->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + frame = &ctx->s_frame; + else + frame = &ctx->d_frame; + + pix = &f->fmt.pix_mp; + frame->fmt = fimc_find_format(&pix->pixelformat, NULL, + get_m2m_fmt_flags(f->type), 0); + if (!frame->fmt) + return -EINVAL; + + /* Update RGB Alpha control state and value range */ + fimc_alpha_ctrl_update(ctx); + + for (i = 0; i < frame->fmt->colplanes; i++) { + frame->payload[i] = + (pix->width * pix->height * frame->fmt->depth[i]) / 8; + } + + fimc_fill_frame(frame, f); + + ctx->scaler.enabled = 1; + + if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + fimc_ctx_state_set(FIMC_PARAMS | FIMC_DST_FMT, ctx); + else + fimc_ctx_state_set(FIMC_PARAMS | FIMC_SRC_FMT, ctx); + + dbg("f_w: %d, f_h: %d", frame->f_width, frame->f_height); + + return 0; +} + +static int fimc_m2m_reqbufs(struct file *file, void *fh, + struct v4l2_requestbuffers *reqbufs) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs); +} + +static int fimc_m2m_querybuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_qbuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_dqbuf(struct file *file, void *fh, + struct v4l2_buffer *buf) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf); +} + +static int fimc_m2m_streamon(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + /* The source and target color format need to be set */ + if (V4L2_TYPE_IS_OUTPUT(type)) { + if (!fimc_ctx_state_is_set(FIMC_SRC_FMT, ctx)) + return -EINVAL; + } else if (!fimc_ctx_state_is_set(FIMC_DST_FMT, ctx)) { + return -EINVAL; + } + + return v4l2_m2m_streamon(file, ctx->m2m_ctx, type); +} + +static int fimc_m2m_streamoff(struct file *file, void *fh, + enum v4l2_buf_type type) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + + return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); +} + +static int fimc_m2m_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cr) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_frame *frame; + + frame = ctx_get_frame(ctx, cr->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + cr->bounds.left = 0; + cr->bounds.top = 0; + cr->bounds.width = frame->o_width; + cr->bounds.height = frame->o_height; + cr->defrect = cr->bounds; + + return 0; +} + +static int fimc_m2m_g_crop(struct file *file, void *fh, struct v4l2_crop *cr) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_frame *frame; + + frame = ctx_get_frame(ctx, cr->type); + if (IS_ERR(frame)) + return PTR_ERR(frame); + + cr->c.left = frame->offs_h; + cr->c.top = frame->offs_v; + cr->c.width = frame->width; + cr->c.height = frame->height; + + return 0; +} + +static int fimc_m2m_try_crop(struct fimc_ctx *ctx, struct v4l2_crop *cr) +{ + struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_frame *f; + u32 min_size, halign, depth = 0; + int i; + + if (cr->c.top < 0 || cr->c.left < 0) { + v4l2_err(fimc->m2m.vfd, + "doesn't support negative values for top & left\n"); + return -EINVAL; + } + if (cr->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) + f = &ctx->d_frame; + else if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) + f = &ctx->s_frame; + else + return -EINVAL; + + min_size = (f == &ctx->s_frame) ? + fimc->variant->min_inp_pixsize : fimc->variant->min_out_pixsize; + + /* Get pixel alignment constraints. */ + if (fimc->variant->min_vsize_align == 1) + halign = fimc_fmt_is_rgb(f->fmt->color) ? 0 : 1; + else + halign = ffs(fimc->variant->min_vsize_align) - 1; + + for (i = 0; i < f->fmt->colplanes; i++) + depth += f->fmt->depth[i]; + + v4l_bound_align_image(&cr->c.width, min_size, f->o_width, + ffs(min_size) - 1, + &cr->c.height, min_size, f->o_height, + halign, 64/(ALIGN(depth, 8))); + + /* adjust left/top if cropping rectangle is out of bounds */ + if (cr->c.left + cr->c.width > f->o_width) + cr->c.left = f->o_width - cr->c.width; + if (cr->c.top + cr->c.height > f->o_height) + cr->c.top = f->o_height - cr->c.height; + + cr->c.left = round_down(cr->c.left, min_size); + cr->c.top = round_down(cr->c.top, fimc->variant->hor_offs_align); + + dbg("l:%d, t:%d, w:%d, h:%d, f_w: %d, f_h: %d", + cr->c.left, cr->c.top, cr->c.width, cr->c.height, + f->f_width, f->f_height); + + return 0; +} + +static int fimc_m2m_s_crop(struct file *file, void *fh, struct v4l2_crop *cr) +{ + struct fimc_ctx *ctx = fh_to_ctx(fh); + struct fimc_dev *fimc = ctx->fimc_dev; + struct fimc_frame *f; + int ret; + + ret = fimc_m2m_try_crop(ctx, cr); + if (ret) + return ret; + + f = (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) ? + &ctx->s_frame : &ctx->d_frame; + + /* Check to see if scaling ratio is within supported range */ + if (fimc_ctx_state_is_set(FIMC_DST_FMT | FIMC_SRC_FMT, ctx)) { + if (cr->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) { + ret = fimc_check_scaler_ratio(ctx, cr->c.width, + cr->c.height, ctx->d_frame.width, + ctx->d_frame.height, ctx->rotation); + } else { + ret = fimc_check_scaler_ratio(ctx, ctx->s_frame.width, + ctx->s_frame.height, cr->c.width, + cr->c.height, ctx->rotation); + } + if (ret) { + v4l2_err(fimc->m2m.vfd, "Out of scaler range\n"); + return -EINVAL; + } + } + + f->offs_h = cr->c.left; + f->offs_v = cr->c.top; + f->width = cr->c.width; + f->height = cr->c.height; + + fimc_ctx_state_set(FIMC_PARAMS, ctx); + + return 0; +} + +static const struct v4l2_ioctl_ops fimc_m2m_ioctl_ops = { + .vidioc_querycap = fimc_m2m_querycap, + .vidioc_enum_fmt_vid_cap_mplane = fimc_m2m_enum_fmt_mplane, + .vidioc_enum_fmt_vid_out_mplane = fimc_m2m_enum_fmt_mplane, + .vidioc_g_fmt_vid_cap_mplane = fimc_m2m_g_fmt_mplane, + .vidioc_g_fmt_vid_out_mplane = fimc_m2m_g_fmt_mplane, + .vidioc_try_fmt_vid_cap_mplane = fimc_m2m_try_fmt_mplane, + .vidioc_try_fmt_vid_out_mplane = fimc_m2m_try_fmt_mplane, + .vidioc_s_fmt_vid_cap_mplane = fimc_m2m_s_fmt_mplane, + .vidioc_s_fmt_vid_out_mplane = fimc_m2m_s_fmt_mplane, + .vidioc_reqbufs = fimc_m2m_reqbufs, + .vidioc_querybuf = fimc_m2m_querybuf, + .vidioc_qbuf = fimc_m2m_qbuf, + .vidioc_dqbuf = fimc_m2m_dqbuf, + .vidioc_streamon = fimc_m2m_streamon, + .vidioc_streamoff = fimc_m2m_streamoff, + .vidioc_g_crop = fimc_m2m_g_crop, + .vidioc_s_crop = fimc_m2m_s_crop, + .vidioc_cropcap = fimc_m2m_cropcap + +}; + +static int queue_init(void *priv, struct vb2_queue *src_vq, + struct vb2_queue *dst_vq) +{ + struct fimc_ctx *ctx = priv; + int ret; + + memset(src_vq, 0, sizeof(*src_vq)); + src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; + src_vq->io_modes = VB2_MMAP | VB2_USERPTR; + src_vq->drv_priv = ctx; + src_vq->ops = &fimc_qops; + src_vq->mem_ops = &vb2_dma_contig_memops; + src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + + ret = vb2_queue_init(src_vq); + if (ret) + return ret; + + memset(dst_vq, 0, sizeof(*dst_vq)); + dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; + dst_vq->io_modes = VB2_MMAP | VB2_USERPTR; + dst_vq->drv_priv = ctx; + dst_vq->ops = &fimc_qops; + dst_vq->mem_ops = &vb2_dma_contig_memops; + dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer); + + return vb2_queue_init(dst_vq); +} + +static int fimc_m2m_open(struct file *file) +{ + struct fimc_dev *fimc = video_drvdata(file); + struct fimc_ctx *ctx; + int ret; + + dbg("pid: %d, state: 0x%lx, refcnt: %d", + task_pid_nr(current), fimc->state, fimc->vid_cap.refcnt); + + /* + * Return if the corresponding video capture node + * is already opened. + */ + if (fimc->vid_cap.refcnt > 0) + return -EBUSY; + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + v4l2_fh_init(&ctx->fh, fimc->m2m.vfd); + ctx->fimc_dev = fimc; + + /* Default color format */ + ctx->s_frame.fmt = fimc_get_format(0); + ctx->d_frame.fmt = fimc_get_format(0); + + ret = fimc_ctrls_create(ctx); + if (ret) + goto error_fh; + + /* Use separate control handler per file handle */ + ctx->fh.ctrl_handler = &ctx->ctrls.handler; + file->private_data = &ctx->fh; + v4l2_fh_add(&ctx->fh); + + /* Setup the device context for memory-to-memory mode */ + ctx->state = FIMC_CTX_M2M; + ctx->flags = 0; + ctx->in_path = FIMC_IO_DMA; + ctx->out_path = FIMC_IO_DMA; + + ctx->m2m_ctx = v4l2_m2m_ctx_init(fimc->m2m.m2m_dev, ctx, queue_init); + if (IS_ERR(ctx->m2m_ctx)) { + ret = PTR_ERR(ctx->m2m_ctx); + goto error_c; + } + + if (fimc->m2m.refcnt++ == 0) + set_bit(ST_M2M_RUN, &fimc->state); + return 0; + +error_c: + fimc_ctrls_delete(ctx); +error_fh: + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + kfree(ctx); + return ret; +} + +static int fimc_m2m_release(struct file *file) +{ + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); + struct fimc_dev *fimc = ctx->fimc_dev; + + dbg("pid: %d, state: 0x%lx, refcnt= %d", + task_pid_nr(current), fimc->state, fimc->m2m.refcnt); + + v4l2_m2m_ctx_release(ctx->m2m_ctx); + fimc_ctrls_delete(ctx); + v4l2_fh_del(&ctx->fh); + v4l2_fh_exit(&ctx->fh); + + if (--fimc->m2m.refcnt <= 0) + clear_bit(ST_M2M_RUN, &fimc->state); + kfree(ctx); + return 0; +} + +static unsigned int fimc_m2m_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); + + return v4l2_m2m_poll(file, ctx->m2m_ctx, wait); +} + + +static int fimc_m2m_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct fimc_ctx *ctx = fh_to_ctx(file->private_data); + + return v4l2_m2m_mmap(file, ctx->m2m_ctx, vma); +} + +static const struct v4l2_file_operations fimc_m2m_fops = { + .owner = THIS_MODULE, + .open = fimc_m2m_open, + .release = fimc_m2m_release, + .poll = fimc_m2m_poll, + .unlocked_ioctl = video_ioctl2, + .mmap = fimc_m2m_mmap, +}; + +static struct v4l2_m2m_ops m2m_ops = { + .device_run = fimc_device_run, + .job_abort = fimc_job_abort, +}; + +int fimc_register_m2m_device(struct fimc_dev *fimc, + struct v4l2_device *v4l2_dev) +{ + struct video_device *vfd; + struct platform_device *pdev; + int ret = 0; + + if (!fimc) + return -ENODEV; + + pdev = fimc->pdev; + fimc->v4l2_dev = v4l2_dev; + + vfd = video_device_alloc(); + if (!vfd) { + v4l2_err(v4l2_dev, "Failed to allocate video device\n"); + return -ENOMEM; + } + + vfd->fops = &fimc_m2m_fops; + vfd->ioctl_ops = &fimc_m2m_ioctl_ops; + vfd->v4l2_dev = v4l2_dev; + vfd->minor = -1; + vfd->release = video_device_release; + vfd->lock = &fimc->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); + + snprintf(vfd->name, sizeof(vfd->name), "fimc.%d.m2m", fimc->id); + video_set_drvdata(vfd, fimc); + + fimc->m2m.vfd = vfd; + fimc->m2m.m2m_dev = v4l2_m2m_init(&m2m_ops); + if (IS_ERR(fimc->m2m.m2m_dev)) { + v4l2_err(v4l2_dev, "failed to initialize v4l2-m2m device\n"); + ret = PTR_ERR(fimc->m2m.m2m_dev); + goto err_init; + } + + ret = media_entity_init(&vfd->entity, 0, NULL, 0); + if (ret) + goto err_me; + + ret = video_register_device(vfd, VFL_TYPE_GRABBER, -1); + if (ret) + goto err_vd; + + v4l2_info(v4l2_dev, "Registered %s as /dev/%s\n", + vfd->name, video_device_node_name(vfd)); + return 0; + +err_vd: + media_entity_cleanup(&vfd->entity); +err_me: + v4l2_m2m_release(fimc->m2m.m2m_dev); +err_init: + video_device_release(fimc->m2m.vfd); + return ret; +} + +void fimc_unregister_m2m_device(struct fimc_dev *fimc) +{ + if (!fimc) + return; + + if (fimc->m2m.m2m_dev) + v4l2_m2m_release(fimc->m2m.m2m_dev); + if (fimc->m2m.vfd) { + media_entity_cleanup(&fimc->m2m.vfd->entity); + /* Can also be called if video device wasn't registered */ + video_unregister_device(fimc->m2m.vfd); + } +} diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.c b/drivers/media/video/s5p-fimc/fimc-mdevice.c index 62ed37e40149..6753c45631b8 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.c +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.c @@ -25,6 +25,7 @@ #include #include "fimc-core.h" +#include "fimc-lite.h" #include "fimc-mdevice.h" #include "mipi-csis.h" @@ -37,22 +38,46 @@ static int __fimc_md_set_camclk(struct fimc_md *fmd, * * Caller holds the graph mutex. */ -void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me) +void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me) { - struct media_entity_graph graph; + struct media_pad *pad = &me->pads[0]; struct v4l2_subdev *sd; + int i; - media_entity_graph_walk_start(&graph, me); + for (i = 0; i < IDX_MAX; i++) + p->subdevs[i] = NULL; - while ((me = media_entity_graph_walk_next(&graph))) { - if (media_entity_type(me) != MEDIA_ENT_T_V4L2_SUBDEV) - continue; - sd = media_entity_to_v4l2_subdev(me); + while (1) { + if (!(pad->flags & MEDIA_PAD_FL_SINK)) + break; - if (sd->grp_id == SENSOR_GROUP_ID) - fimc->pipeline.sensor = sd; - else if (sd->grp_id == CSIS_GROUP_ID) - fimc->pipeline.csis = sd; + /* source pad */ + pad = media_entity_remote_source(pad); + if (pad == NULL || + media_entity_type(pad->entity) != MEDIA_ENT_T_V4L2_SUBDEV) + break; + + sd = media_entity_to_v4l2_subdev(pad->entity); + + switch (sd->grp_id) { + case SENSOR_GROUP_ID: + p->subdevs[IDX_SENSOR] = sd; + break; + case CSIS_GROUP_ID: + p->subdevs[IDX_CSIS] = sd; + break; + case FLITE_GROUP_ID: + p->subdevs[IDX_FLITE] = sd; + break; + case FIMC_GROUP_ID: + /* No need to control FIMC subdev through subdev ops */ + break; + default: + pr_warn("%s: Unknown subdev grp_id: %#x\n", + __func__, sd->grp_id); + } + /* sink pad */ + pad = &sd->entity.pads[0]; } } @@ -85,30 +110,27 @@ static int __subdev_set_power(struct v4l2_subdev *sd, int on) /** * fimc_pipeline_s_power - change power state of all pipeline subdevs * @fimc: fimc device terminating the pipeline - * @state: 1 to enable power or 0 for power down + * @state: true to power on, false to power off * - * Need to be called with the graph mutex held. + * Needs to be called with the graph mutex held. */ -int fimc_pipeline_s_power(struct fimc_dev *fimc, int state) +int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state) { - int ret = 0; + unsigned int i; + int ret; - if (fimc->pipeline.sensor == NULL) + if (p->subdevs[IDX_SENSOR] == NULL) return -ENXIO; - if (state) { - ret = __subdev_set_power(fimc->pipeline.csis, 1); - if (ret && ret != -ENXIO) + for (i = 0; i < IDX_MAX; i++) { + unsigned int idx = state ? (IDX_MAX - 1) - i : i; + + ret = __subdev_set_power(p->subdevs[idx], state); + if (ret < 0 && ret != -ENXIO) return ret; - return __subdev_set_power(fimc->pipeline.sensor, 1); } - ret = __subdev_set_power(fimc->pipeline.sensor, 0); - if (ret) - return ret; - ret = __subdev_set_power(fimc->pipeline.csis, 0); - - return ret == -ENXIO ? 0 : ret; + return 0; } /** @@ -119,32 +141,36 @@ int fimc_pipeline_s_power(struct fimc_dev *fimc, int state) * * This function must be called with the graph mutex held. */ -static int __fimc_pipeline_initialize(struct fimc_dev *fimc, +static int __fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me, bool prep) { int ret; if (prep) - fimc_pipeline_prepare(fimc, me); - if (fimc->pipeline.sensor == NULL) + fimc_pipeline_prepare(p, me); + + if (p->subdevs[IDX_SENSOR] == NULL) return -EINVAL; - ret = fimc_md_set_camclk(fimc->pipeline.sensor, true); + + ret = fimc_md_set_camclk(p->subdevs[IDX_SENSOR], true); if (ret) return ret; - return fimc_pipeline_s_power(fimc, 1); + + return fimc_pipeline_s_power(p, 1); } -int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, +int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me, bool prep) { int ret; mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_initialize(fimc, me, prep); + ret = __fimc_pipeline_initialize(p, me, prep); mutex_unlock(&me->parent->graph_mutex); return ret; } +EXPORT_SYMBOL_GPL(fimc_pipeline_initialize); /** * __fimc_pipeline_shutdown - disable the sensor clock and pipeline power @@ -154,52 +180,55 @@ int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, * sensor clock. * Called with the graph mutex held. */ -int __fimc_pipeline_shutdown(struct fimc_dev *fimc) +int __fimc_pipeline_shutdown(struct fimc_pipeline *p) { int ret = 0; - if (fimc->pipeline.sensor) { - ret = fimc_pipeline_s_power(fimc, 0); - fimc_md_set_camclk(fimc->pipeline.sensor, false); + if (p->subdevs[IDX_SENSOR]) { + ret = fimc_pipeline_s_power(p, 0); + fimc_md_set_camclk(p->subdevs[IDX_SENSOR], false); } return ret == -ENXIO ? 0 : ret; } -int fimc_pipeline_shutdown(struct fimc_dev *fimc) +int fimc_pipeline_shutdown(struct fimc_pipeline *p) { - struct media_entity *me = &fimc->vid_cap.vfd->entity; + struct media_entity *me = &p->subdevs[IDX_SENSOR]->entity; int ret; mutex_lock(&me->parent->graph_mutex); - ret = __fimc_pipeline_shutdown(fimc); + ret = __fimc_pipeline_shutdown(p); mutex_unlock(&me->parent->graph_mutex); return ret; } +EXPORT_SYMBOL_GPL(fimc_pipeline_shutdown); /** * fimc_pipeline_s_stream - invoke s_stream on pipeline subdevs - * @fimc: fimc device terminating the pipeline + * @pipeline: video pipeline structure * @on: passed as the s_stream call argument */ -int fimc_pipeline_s_stream(struct fimc_dev *fimc, int on) +int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool on) { - struct fimc_pipeline *p = &fimc->pipeline; - int ret = 0; + int i, ret; - if (p->sensor == NULL) + if (p->subdevs[IDX_SENSOR] == NULL) return -ENODEV; - if ((on && p->csis) || !on) - ret = v4l2_subdev_call(on ? p->csis : p->sensor, - video, s_stream, on); - if (ret < 0 && ret != -ENOIOCTLCMD) - return ret; - if ((!on && p->csis) || on) - ret = v4l2_subdev_call(on ? p->sensor : p->csis, - video, s_stream, on); - return ret == -ENOIOCTLCMD ? 0 : ret; + for (i = 0; i < IDX_MAX; i++) { + unsigned int idx = on ? (IDX_MAX - 1) - i : i; + + ret = v4l2_subdev_call(p->subdevs[idx], video, s_stream, on); + + if (ret < 0 && ret != -ENOIOCTLCMD && ret != -ENODEV) + return ret; + } + + return 0; + } +EXPORT_SYMBOL_GPL(fimc_pipeline_s_stream); /* * Sensor subdevice helper functions @@ -214,14 +243,20 @@ static struct v4l2_subdev *fimc_md_register_sensor(struct fimc_md *fmd, return NULL; adapter = i2c_get_adapter(s_info->pdata->i2c_bus_num); - if (!adapter) - return NULL; + if (!adapter) { + v4l2_warn(&fmd->v4l2_dev, + "Failed to get I2C adapter %d, deferring probe\n", + s_info->pdata->i2c_bus_num); + return ERR_PTR(-EPROBE_DEFER); + } sd = v4l2_i2c_new_subdev_board(&fmd->v4l2_dev, adapter, s_info->pdata->board_info, NULL); if (IS_ERR_OR_NULL(sd)) { i2c_put_adapter(adapter); - v4l2_err(&fmd->v4l2_dev, "Failed to acquire subdev\n"); - return NULL; + v4l2_warn(&fmd->v4l2_dev, + "Failed to acquire subdev %s, deferring probe\n", + s_info->pdata->board_info->type); + return ERR_PTR(-EPROBE_DEFER); } v4l2_set_subdev_hostdata(sd, s_info); sd->grp_id = SENSOR_GROUP_ID; @@ -269,13 +304,22 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) fmd->num_sensors = num_clients; for (i = 0; i < num_clients; i++) { + struct v4l2_subdev *sd; + fmd->sensor[i].pdata = &pdata->isp_info[i]; ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], true); if (ret) break; - fmd->sensor[i].subdev = - fimc_md_register_sensor(fmd, &fmd->sensor[i]); + sd = fimc_md_register_sensor(fmd, &fmd->sensor[i]); ret = __fimc_md_set_camclk(fmd, &fmd->sensor[i], false); + + if (!IS_ERR(sd)) { + fmd->sensor[i].subdev = sd; + } else { + fmd->sensor[i].subdev = NULL; + ret = PTR_ERR(sd); + break; + } if (ret) break; } @@ -289,21 +333,50 @@ static int fimc_md_register_sensor_entities(struct fimc_md *fmd) static int fimc_register_callback(struct device *dev, void *p) { struct fimc_dev *fimc = dev_get_drvdata(dev); + struct v4l2_subdev *sd = &fimc->vid_cap.subdev; struct fimc_md *fmd = p; - int ret; + int ret = 0; if (!fimc || !fimc->pdev) return 0; + if (fimc->pdev->id < 0 || fimc->pdev->id >= FIMC_MAX_DEVS) return 0; fmd->fimc[fimc->pdev->id] = fimc; - ret = fimc_register_m2m_device(fimc, &fmd->v4l2_dev); - if (ret) - return ret; - ret = fimc_register_capture_device(fimc, &fmd->v4l2_dev); - if (!ret) - fimc->vid_cap.user_subdev_api = fmd->user_subdev_api; + sd->grp_id = FIMC_GROUP_ID; + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) { + v4l2_err(&fmd->v4l2_dev, "Failed to register FIMC.%d (%d)\n", + fimc->id, ret); + } + + return ret; +} + +static int fimc_lite_register_callback(struct device *dev, void *p) +{ + struct fimc_lite *fimc = dev_get_drvdata(dev); + struct v4l2_subdev *sd = &fimc->subdev; + struct fimc_md *fmd = p; + int ret; + + if (fimc == NULL) + return 0; + + if (fimc->index >= FIMC_LITE_MAX_DEVS) + return 0; + + fmd->fimc_lite[fimc->index] = fimc; + sd->grp_id = FLITE_GROUP_ID; + + ret = v4l2_device_register_subdev(&fmd->v4l2_dev, sd); + if (ret) { + v4l2_err(&fmd->v4l2_dev, + "Failed to register FIMC-LITE.%d (%d)\n", + fimc->index, ret); + } return ret; } @@ -336,22 +409,56 @@ static int csis_register_callback(struct device *dev, void *p) */ static int fimc_md_register_platform_entities(struct fimc_md *fmd) { + struct s5p_platform_fimc *pdata = fmd->pdev->dev.platform_data; struct device_driver *driver; - int ret; + int ret, i; driver = driver_find(FIMC_MODULE_NAME, &platform_bus_type); - if (!driver) - return -ENODEV; + if (!driver) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + FIMC_MODULE_NAME); + return -EPROBE_DEFER; + } + ret = driver_for_each_device(driver, NULL, fmd, fimc_register_callback); if (ret) return ret; - driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); - if (driver) + driver = driver_find(FIMC_LITE_DRV_NAME, &platform_bus_type); + if (driver && try_module_get(driver->owner)) { ret = driver_for_each_device(driver, NULL, fmd, - csis_register_callback); - return ret; + fimc_lite_register_callback); + if (ret) + return ret; + module_put(driver->owner); + } + /* + * Check if there is any sensor on the MIPI-CSI2 bus and + * if not skip the s5p-csis module loading. + */ + if (pdata == NULL) + return 0; + for (i = 0; i < pdata->num_clients; i++) { + if (pdata->isp_info[i].bus_type == FIMC_MIPI_CSI2) { + ret = 1; + break; + } + } + if (!ret) + return 0; + + driver = driver_find(CSIS_DRIVER_NAME, &platform_bus_type); + if (!driver || !try_module_get(driver->owner)) { + v4l2_warn(&fmd->v4l2_dev, + "%s driver not found, deffering probe\n", + CSIS_DRIVER_NAME); + return -EPROBE_DEFER; + } + + return driver_for_each_device(driver, NULL, fmd, + csis_register_callback); } static void fimc_md_unregister_entities(struct fimc_md *fmd) @@ -361,14 +468,20 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) for (i = 0; i < FIMC_MAX_DEVS; i++) { if (fmd->fimc[i] == NULL) continue; - fimc_unregister_m2m_device(fmd->fimc[i]); - fimc_unregister_capture_device(fmd->fimc[i]); + v4l2_device_unregister_subdev(&fmd->fimc[i]->vid_cap.subdev); fmd->fimc[i] = NULL; } + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + if (fmd->fimc_lite[i] == NULL) + continue; + v4l2_device_unregister_subdev(&fmd->fimc_lite[i]->subdev); + fmd->fimc_lite[i] = NULL; + } for (i = 0; i < CSIS_MAX_ENTITIES; i++) { if (fmd->csis[i].sd == NULL) continue; v4l2_device_unregister_subdev(fmd->csis[i].sd); + module_put(fmd->csis[i].sd->owner); fmd->csis[i].sd = NULL; } for (i = 0; i < fmd->num_sensors; i++) { @@ -379,35 +492,6 @@ static void fimc_md_unregister_entities(struct fimc_md *fmd) } } -static int fimc_md_register_video_nodes(struct fimc_md *fmd) -{ - struct video_device *vdev; - int i, ret = 0; - - for (i = 0; i < FIMC_MAX_DEVS && !ret; i++) { - if (!fmd->fimc[i]) - continue; - - vdev = fmd->fimc[i]->m2m.vfd; - if (vdev) { - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - if (ret) - break; - v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n", - vdev->name, video_device_node_name(vdev)); - } - - vdev = fmd->fimc[i]->vid_cap.vfd; - if (vdev == NULL) - continue; - ret = video_register_device(vdev, VFL_TYPE_GRABBER, -1); - v4l2_info(&fmd->v4l2_dev, "Registered %s as /dev/%s\n", - vdev->name, video_device_node_name(vdev)); - } - - return ret; -} - /** * __fimc_md_create_fimc_links - create links to all FIMC entities * @fmd: fimc media device @@ -416,29 +500,29 @@ static int fimc_md_register_video_nodes(struct fimc_md *fmd) * @pad: the source entity pad index * @fimc_id: index of the fimc device for which link should be enabled */ -static int __fimc_md_create_fimc_links(struct fimc_md *fmd, - struct media_entity *source, - struct v4l2_subdev *sensor, - int pad, int fimc_id) +static int __fimc_md_create_fimc_sink_links(struct fimc_md *fmd, + struct media_entity *source, + struct v4l2_subdev *sensor, + int pad, int fimc_id) { struct fimc_sensor_info *s_info; struct media_entity *sink; - unsigned int flags; + unsigned int flags = 0; int ret, i; for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) - break; + continue; /* * Some FIMC variants are not fitted with camera capture * interface. Skip creating a link from sensor for those. */ - if (sensor->grp_id == SENSOR_GROUP_ID && - !fmd->fimc[i]->variant->has_cam_if) + if (!fmd->fimc[i]->variant->has_cam_if) continue; flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; - sink = &fmd->fimc[i]->vid_cap.subdev->entity; + + sink = &fmd->fimc[i]->vid_cap.subdev.entity; ret = media_entity_create_link(source, pad, sink, FIMC_SD_PAD_SINK, flags); if (ret) @@ -453,7 +537,7 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", source->name, flags ? '=' : '-', sink->name); - if (flags == 0) + if (flags == 0 || sensor == NULL) continue; s_info = v4l2_get_subdev_hostdata(sensor); if (!WARN_ON(s_info == NULL)) { @@ -463,9 +547,55 @@ static int __fimc_md_create_fimc_links(struct fimc_md *fmd, spin_unlock_irqrestore(&fmd->slock, irq_flags); } } + + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + if (!fmd->fimc_lite[i]) + continue; + + flags = (i == fimc_id) ? MEDIA_LNK_FL_ENABLED : 0; + + sink = &fmd->fimc_lite[i]->subdev.entity; + ret = media_entity_create_link(source, pad, sink, + FLITE_SD_PAD_SINK, flags); + if (ret) + return ret; + + /* Notify FIMC-LITE subdev entity */ + ret = media_entity_call(sink, link_setup, &sink->pads[0], + &source->pads[pad], flags); + if (ret) + break; + + v4l2_info(&fmd->v4l2_dev, "created link [%s] %c> [%s]", + source->name, flags ? '=' : '-', sink->name); + } return 0; } +/* Create links from FIMC-LITE source pads to other entities */ +static int __fimc_md_create_flite_source_links(struct fimc_md *fmd) +{ + struct media_entity *source, *sink; + unsigned int flags = MEDIA_LNK_FL_ENABLED; + int i, ret; + + for (i = 0; i < FIMC_LITE_MAX_DEVS; i++) { + struct fimc_lite *fimc = fmd->fimc_lite[i]; + if (fimc == NULL) + continue; + source = &fimc->subdev.entity; + sink = &fimc->vfd->entity; + /* FIMC-LITE's subdev and video node */ + ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, + sink, 0, flags); + if (ret) + break; + /* TODO: create links to other entities */ + } + + return ret; +} + /** * fimc_md_create_links - create default links between registered entities * @@ -522,8 +652,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) v4l2_info(&fmd->v4l2_dev, "created link [%s] => [%s]", sensor->entity.name, csis->entity.name); - source = &csis->entity; - pad = CSIS_PAD_SOURCE; + source = NULL; break; case FIMC_ITU_601...FIMC_ITU_656: @@ -539,15 +668,27 @@ static int fimc_md_create_links(struct fimc_md *fmd) if (source == NULL) continue; - ret = __fimc_md_create_fimc_links(fmd, source, sensor, pad, - fimc_id++); + ret = __fimc_md_create_fimc_sink_links(fmd, source, sensor, + pad, fimc_id++); } + + fimc_id = 0; + for (i = 0; i < ARRAY_SIZE(fmd->csis); i++) { + if (fmd->csis[i].sd == NULL) + continue; + source = &fmd->csis[i].sd->entity; + pad = CSIS_PAD_SOURCE; + + ret = __fimc_md_create_fimc_sink_links(fmd, source, NULL, + pad, fimc_id++); + } + /* Create immutable links between each FIMC's subdev and video node */ flags = MEDIA_LNK_FL_IMMUTABLE | MEDIA_LNK_FL_ENABLED; for (i = 0; i < FIMC_MAX_DEVS; i++) { if (!fmd->fimc[i]) continue; - source = &fmd->fimc[i]->vid_cap.subdev->entity; + source = &fmd->fimc[i]->vid_cap.subdev.entity; sink = &fmd->fimc[i]->vid_cap.vfd->entity; ret = media_entity_create_link(source, FIMC_SD_PAD_SOURCE, sink, 0, flags); @@ -555,7 +696,7 @@ static int fimc_md_create_links(struct fimc_md *fmd) break; } - return ret; + return __fimc_md_create_flite_source_links(fmd); } /* @@ -663,24 +804,40 @@ int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on) static int fimc_md_link_notify(struct media_pad *source, struct media_pad *sink, u32 flags) { + struct fimc_lite *fimc_lite = NULL; + struct fimc_dev *fimc = NULL; + struct fimc_pipeline *pipeline; struct v4l2_subdev *sd; - struct fimc_dev *fimc; int ret = 0; if (media_entity_type(sink->entity) != MEDIA_ENT_T_V4L2_SUBDEV) return 0; sd = media_entity_to_v4l2_subdev(sink->entity); - fimc = v4l2_get_subdevdata(sd); + + switch (sd->grp_id) { + case FLITE_GROUP_ID: + fimc_lite = v4l2_get_subdevdata(sd); + pipeline = &fimc_lite->pipeline; + break; + case FIMC_GROUP_ID: + fimc = v4l2_get_subdevdata(sd); + pipeline = &fimc->pipeline; + break; + default: + return 0; + } if (!(flags & MEDIA_LNK_FL_ENABLED)) { - ret = __fimc_pipeline_shutdown(fimc); - fimc->pipeline.sensor = NULL; - fimc->pipeline.csis = NULL; + ret = __fimc_pipeline_shutdown(pipeline); + pipeline->subdevs[IDX_SENSOR] = NULL; + pipeline->subdevs[IDX_CSIS] = NULL; - mutex_lock(&fimc->lock); - fimc_ctrls_delete(fimc->vid_cap.ctx); - mutex_unlock(&fimc->lock); + if (fimc) { + mutex_lock(&fimc->lock); + fimc_ctrls_delete(fimc->vid_cap.ctx); + mutex_unlock(&fimc->lock); + } return ret; } /* @@ -688,14 +845,23 @@ static int fimc_md_link_notify(struct media_pad *source, * pipeline is already in use, i.e. its video node is opened. * Recreate the controls destroyed during the link deactivation. */ - mutex_lock(&fimc->lock); - if (fimc->vid_cap.refcnt > 0) { - ret = __fimc_pipeline_initialize(fimc, source->entity, true); + if (fimc) { + mutex_lock(&fimc->lock); + if (fimc->vid_cap.refcnt > 0) { + ret = __fimc_pipeline_initialize(pipeline, + source->entity, true); if (!ret) ret = fimc_capture_ctrls_create(fimc); + } + mutex_unlock(&fimc->lock); + } else { + mutex_lock(&fimc_lite->lock); + if (fimc_lite->ref_count > 0) { + ret = __fimc_pipeline_initialize(pipeline, + source->entity, true); + } + mutex_unlock(&fimc_lite->lock); } - mutex_unlock(&fimc->lock); - return ret ? -EPIPE : ret; } @@ -744,7 +910,7 @@ static ssize_t fimc_md_sysfs_store(struct device *dev, static DEVICE_ATTR(subdev_conf_mode, S_IWUSR | S_IRUGO, fimc_md_sysfs_show, fimc_md_sysfs_store); -static int __devinit fimc_md_probe(struct platform_device *pdev) +static int fimc_md_probe(struct platform_device *pdev) { struct v4l2_device *v4l2_dev; struct fimc_md *fmd; @@ -776,42 +942,48 @@ static int __devinit fimc_md_probe(struct platform_device *pdev) ret = media_device_register(&fmd->media_dev); if (ret < 0) { v4l2_err(v4l2_dev, "Failed to register media device: %d\n", ret); - goto err2; + goto err_md; } ret = fimc_md_get_clocks(fmd); if (ret) - goto err3; + goto err_clk; fmd->user_subdev_api = false; + + /* Protect the media graph while we're registering entities */ + mutex_lock(&fmd->media_dev.graph_mutex); + ret = fimc_md_register_platform_entities(fmd); if (ret) - goto err3; + goto err_unlock; if (pdev->dev.platform_data) { ret = fimc_md_register_sensor_entities(fmd); if (ret) - goto err3; + goto err_unlock; } ret = fimc_md_create_links(fmd); if (ret) - goto err3; + goto err_unlock; ret = v4l2_device_register_subdev_nodes(&fmd->v4l2_dev); if (ret) - goto err3; - ret = fimc_md_register_video_nodes(fmd); - if (ret) - goto err3; + goto err_unlock; ret = device_create_file(&pdev->dev, &dev_attr_subdev_conf_mode); - if (!ret) { - platform_set_drvdata(pdev, fmd); - return 0; - } -err3: + if (ret) + goto err_unlock; + + platform_set_drvdata(pdev, fmd); + mutex_unlock(&fmd->media_dev.graph_mutex); + return 0; + +err_unlock: + mutex_unlock(&fmd->media_dev.graph_mutex); +err_clk: media_device_unregister(&fmd->media_dev); fimc_md_put_clocks(fmd); fimc_md_unregister_entities(fmd); -err2: +err_md: v4l2_device_unregister(&fmd->v4l2_dev); return ret; } @@ -841,10 +1013,12 @@ static struct platform_driver fimc_md_driver = { int __init fimc_md_init(void) { int ret; + request_module("s5p-csis"); ret = fimc_register_driver(); if (ret) return ret; + return platform_driver_register(&fimc_md_driver); } void __exit fimc_md_exit(void) diff --git a/drivers/media/video/s5p-fimc/fimc-mdevice.h b/drivers/media/video/s5p-fimc/fimc-mdevice.h index da3780823e7d..3b8a3492a176 100644 --- a/drivers/media/video/s5p-fimc/fimc-mdevice.h +++ b/drivers/media/video/s5p-fimc/fimc-mdevice.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 Samsung Electronics Co., Ltd. + * Copyright (C) 2011 - 2012 Samsung Electronics Co., Ltd. * * 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 @@ -18,12 +18,15 @@ #include #include "fimc-core.h" +#include "fimc-lite.h" #include "mipi-csis.h" -/* Group IDs of sensor, MIPI CSIS and the writeback subdevs. */ +/* Group IDs of sensor, MIPI-CSIS, FIMC-LITE and the writeback subdevs. */ #define SENSOR_GROUP_ID (1 << 8) #define CSIS_GROUP_ID (1 << 9) #define WRITEBACK_GROUP_ID (1 << 10) +#define FIMC_GROUP_ID (1 << 11) +#define FLITE_GROUP_ID (1 << 12) #define FIMC_MAX_SENSORS 8 #define FIMC_MAX_CAMCLKS 2 @@ -73,6 +76,7 @@ struct fimc_md { struct fimc_sensor_info sensor[FIMC_MAX_SENSORS]; int num_sensors; struct fimc_camclk_info camclk[FIMC_MAX_CAMCLKS]; + struct fimc_lite *fimc_lite[FIMC_LITE_MAX_DEVS]; struct fimc_dev *fimc[FIMC_MAX_DEVS]; struct media_device media_dev; struct v4l2_device v4l2_dev; @@ -108,11 +112,11 @@ static inline void fimc_md_graph_unlock(struct fimc_dev *fimc) } int fimc_md_set_camclk(struct v4l2_subdev *sd, bool on); -void fimc_pipeline_prepare(struct fimc_dev *fimc, struct media_entity *me); -int fimc_pipeline_initialize(struct fimc_dev *fimc, struct media_entity *me, +void fimc_pipeline_prepare(struct fimc_pipeline *p, struct media_entity *me); +int fimc_pipeline_initialize(struct fimc_pipeline *p, struct media_entity *me, bool resume); -int fimc_pipeline_shutdown(struct fimc_dev *fimc); -int fimc_pipeline_s_power(struct fimc_dev *fimc, int state); -int fimc_pipeline_s_stream(struct fimc_dev *fimc, int state); +int fimc_pipeline_shutdown(struct fimc_pipeline *p); +int fimc_pipeline_s_power(struct fimc_pipeline *p, bool state); +int fimc_pipeline_s_stream(struct fimc_pipeline *p, bool state); #endif diff --git a/drivers/media/video/s5p-fimc/fimc-reg.c b/drivers/media/video/s5p-fimc/fimc-reg.c index 15466d0529c1..1fc4ce8446f5 100644 --- a/drivers/media/video/s5p-fimc/fimc-reg.c +++ b/drivers/media/video/s5p-fimc/fimc-reg.c @@ -1,9 +1,8 @@ /* * Register interface file for Samsung Camera Interface (FIMC) driver * - * Copyright (c) 2010 Samsung Electronics - * - * Sylwester Nawrocki, s.nawrocki@samsung.com + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. + * Sylwester Nawrocki, * * 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 @@ -12,9 +11,9 @@ #include #include -#include #include +#include "fimc-reg.h" #include "fimc-core.h" @@ -22,19 +21,19 @@ void fimc_hw_reset(struct fimc_dev *dev) { u32 cfg; - cfg = readl(dev->regs + S5P_CISRCFMT); - cfg |= S5P_CISRCFMT_ITU601_8BIT; - writel(cfg, dev->regs + S5P_CISRCFMT); + cfg = readl(dev->regs + FIMC_REG_CISRCFMT); + cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; + writel(cfg, dev->regs + FIMC_REG_CISRCFMT); /* Software reset. */ - cfg = readl(dev->regs + S5P_CIGCTRL); - cfg |= (S5P_CIGCTRL_SWRST | S5P_CIGCTRL_IRQ_LEVEL); - writel(cfg, dev->regs + S5P_CIGCTRL); + cfg = readl(dev->regs + FIMC_REG_CIGCTRL); + cfg |= (FIMC_REG_CIGCTRL_SWRST | FIMC_REG_CIGCTRL_IRQ_LEVEL); + writel(cfg, dev->regs + FIMC_REG_CIGCTRL); udelay(10); - cfg = readl(dev->regs + S5P_CIGCTRL); - cfg &= ~S5P_CIGCTRL_SWRST; - writel(cfg, dev->regs + S5P_CIGCTRL); + cfg = readl(dev->regs + FIMC_REG_CIGCTRL); + cfg &= ~FIMC_REG_CIGCTRL_SWRST; + writel(cfg, dev->regs + FIMC_REG_CIGCTRL); if (dev->variant->out_buf_count > 4) fimc_hw_set_dma_seq(dev, 0xF); @@ -42,32 +41,32 @@ void fimc_hw_reset(struct fimc_dev *dev) static u32 fimc_hw_get_in_flip(struct fimc_ctx *ctx) { - u32 flip = S5P_MSCTRL_FLIP_NORMAL; + u32 flip = FIMC_REG_MSCTRL_FLIP_NORMAL; if (ctx->hflip) - flip = S5P_MSCTRL_FLIP_X_MIRROR; + flip = FIMC_REG_MSCTRL_FLIP_X_MIRROR; if (ctx->vflip) - flip = S5P_MSCTRL_FLIP_Y_MIRROR; + flip = FIMC_REG_MSCTRL_FLIP_Y_MIRROR; if (ctx->rotation <= 90) return flip; - return (flip ^ S5P_MSCTRL_FLIP_180) & S5P_MSCTRL_FLIP_180; + return (flip ^ FIMC_REG_MSCTRL_FLIP_180) & FIMC_REG_MSCTRL_FLIP_180; } static u32 fimc_hw_get_target_flip(struct fimc_ctx *ctx) { - u32 flip = S5P_CITRGFMT_FLIP_NORMAL; + u32 flip = FIMC_REG_CITRGFMT_FLIP_NORMAL; if (ctx->hflip) - flip |= S5P_CITRGFMT_FLIP_X_MIRROR; + flip |= FIMC_REG_CITRGFMT_FLIP_X_MIRROR; if (ctx->vflip) - flip |= S5P_CITRGFMT_FLIP_Y_MIRROR; + flip |= FIMC_REG_CITRGFMT_FLIP_Y_MIRROR; if (ctx->rotation <= 90) return flip; - return (flip ^ S5P_CITRGFMT_FLIP_180) & S5P_CITRGFMT_FLIP_180; + return (flip ^ FIMC_REG_CITRGFMT_FLIP_180) & FIMC_REG_CITRGFMT_FLIP_180; } void fimc_hw_set_rotation(struct fimc_ctx *ctx) @@ -75,9 +74,9 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx) u32 cfg, flip; struct fimc_dev *dev = ctx->fimc_dev; - cfg = readl(dev->regs + S5P_CITRGFMT); - cfg &= ~(S5P_CITRGFMT_INROT90 | S5P_CITRGFMT_OUTROT90 | - S5P_CITRGFMT_FLIP_180); + cfg = readl(dev->regs + FIMC_REG_CITRGFMT); + cfg &= ~(FIMC_REG_CITRGFMT_INROT90 | FIMC_REG_CITRGFMT_OUTROT90 | + FIMC_REG_CITRGFMT_FLIP_180); /* * The input and output rotator cannot work simultaneously. @@ -85,21 +84,21 @@ void fimc_hw_set_rotation(struct fimc_ctx *ctx) * in direct fifo output mode. */ if (ctx->rotation == 90 || ctx->rotation == 270) { - if (ctx->out_path == FIMC_LCDFIFO) - cfg |= S5P_CITRGFMT_INROT90; + if (ctx->out_path == FIMC_IO_LCDFIFO) + cfg |= FIMC_REG_CITRGFMT_INROT90; else - cfg |= S5P_CITRGFMT_OUTROT90; + cfg |= FIMC_REG_CITRGFMT_OUTROT90; } - if (ctx->out_path == FIMC_DMA) { + if (ctx->out_path == FIMC_IO_DMA) { cfg |= fimc_hw_get_target_flip(ctx); - writel(cfg, dev->regs + S5P_CITRGFMT); + writel(cfg, dev->regs + FIMC_REG_CITRGFMT); } else { /* LCD FIFO path */ - flip = readl(dev->regs + S5P_MSCTRL); - flip &= ~S5P_MSCTRL_FLIP_MASK; + flip = readl(dev->regs + FIMC_REG_MSCTRL); + flip &= ~FIMC_REG_MSCTRL_FLIP_MASK; flip |= fimc_hw_get_in_flip(ctx); - writel(flip, dev->regs + S5P_MSCTRL); + writel(flip, dev->regs + FIMC_REG_MSCTRL); } } @@ -110,43 +109,40 @@ void fimc_hw_set_target_format(struct fimc_ctx *ctx) struct fimc_frame *frame = &ctx->d_frame; dbg("w= %d, h= %d color: %d", frame->width, - frame->height, frame->fmt->color); + frame->height, frame->fmt->color); - cfg = readl(dev->regs + S5P_CITRGFMT); - cfg &= ~(S5P_CITRGFMT_FMT_MASK | S5P_CITRGFMT_HSIZE_MASK | - S5P_CITRGFMT_VSIZE_MASK); + cfg = readl(dev->regs + FIMC_REG_CITRGFMT); + cfg &= ~(FIMC_REG_CITRGFMT_FMT_MASK | FIMC_REG_CITRGFMT_HSIZE_MASK | + FIMC_REG_CITRGFMT_VSIZE_MASK); switch (frame->fmt->color) { - case S5P_FIMC_RGB444...S5P_FIMC_RGB888: - cfg |= S5P_CITRGFMT_RGB; + case FIMC_FMT_RGB444...FIMC_FMT_RGB888: + cfg |= FIMC_REG_CITRGFMT_RGB; break; - case S5P_FIMC_YCBCR420: - cfg |= S5P_CITRGFMT_YCBCR420; + case FIMC_FMT_YCBCR420: + cfg |= FIMC_REG_CITRGFMT_YCBCR420; break; - case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422: + case FIMC_FMT_YCBYCR422...FIMC_FMT_CRYCBY422: if (frame->fmt->colplanes == 1) - cfg |= S5P_CITRGFMT_YCBCR422_1P; + cfg |= FIMC_REG_CITRGFMT_YCBCR422_1P; else - cfg |= S5P_CITRGFMT_YCBCR422; + cfg |= FIMC_REG_CITRGFMT_YCBCR422; break; default: break; } - if (ctx->rotation == 90 || ctx->rotation == 270) { - cfg |= S5P_CITRGFMT_HSIZE(frame->height); - cfg |= S5P_CITRGFMT_VSIZE(frame->width); - } else { + if (ctx->rotation == 90 || ctx->rotation == 270) + cfg |= (frame->height << 16) | frame->width; + else + cfg |= (frame->width << 16) | frame->height; - cfg |= S5P_CITRGFMT_HSIZE(frame->width); - cfg |= S5P_CITRGFMT_VSIZE(frame->height); - } + writel(cfg, dev->regs + FIMC_REG_CITRGFMT); - writel(cfg, dev->regs + S5P_CITRGFMT); - - cfg = readl(dev->regs + S5P_CITAREA) & ~S5P_CITAREA_MASK; + cfg = readl(dev->regs + FIMC_REG_CITAREA); + cfg &= ~FIMC_REG_CITAREA_MASK; cfg |= (frame->width * frame->height); - writel(cfg, dev->regs + S5P_CITAREA); + writel(cfg, dev->regs + FIMC_REG_CITAREA); } static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx) @@ -155,87 +151,82 @@ static void fimc_hw_set_out_dma_size(struct fimc_ctx *ctx) struct fimc_frame *frame = &ctx->d_frame; u32 cfg; - cfg = S5P_ORIG_SIZE_HOR(frame->f_width); - cfg |= S5P_ORIG_SIZE_VER(frame->f_height); - writel(cfg, dev->regs + S5P_ORGOSIZE); + cfg = (frame->f_height << 16) | frame->f_width; + writel(cfg, dev->regs + FIMC_REG_ORGOSIZE); /* Select color space conversion equation (HD/SD size).*/ - cfg = readl(dev->regs + S5P_CIGCTRL); + cfg = readl(dev->regs + FIMC_REG_CIGCTRL); if (frame->f_width >= 1280) /* HD */ - cfg |= S5P_CIGCTRL_CSC_ITU601_709; + cfg |= FIMC_REG_CIGCTRL_CSC_ITU601_709; else /* SD */ - cfg &= ~S5P_CIGCTRL_CSC_ITU601_709; - writel(cfg, dev->regs + S5P_CIGCTRL); + cfg &= ~FIMC_REG_CIGCTRL_CSC_ITU601_709; + writel(cfg, dev->regs + FIMC_REG_CIGCTRL); } void fimc_hw_set_out_dma(struct fimc_ctx *ctx) { - u32 cfg; struct fimc_dev *dev = ctx->fimc_dev; struct fimc_frame *frame = &ctx->d_frame; struct fimc_dma_offset *offset = &frame->dma_offset; struct fimc_fmt *fmt = frame->fmt; + u32 cfg; /* Set the input dma offsets. */ - cfg = 0; - cfg |= S5P_CIO_OFFS_HOR(offset->y_h); - cfg |= S5P_CIO_OFFS_VER(offset->y_v); - writel(cfg, dev->regs + S5P_CIOYOFF); + cfg = (offset->y_v << 16) | offset->y_h; + writel(cfg, dev->regs + FIMC_REG_CIOYOFF); - cfg = 0; - cfg |= S5P_CIO_OFFS_HOR(offset->cb_h); - cfg |= S5P_CIO_OFFS_VER(offset->cb_v); - writel(cfg, dev->regs + S5P_CIOCBOFF); + cfg = (offset->cb_v << 16) | offset->cb_h; + writel(cfg, dev->regs + FIMC_REG_CIOCBOFF); - cfg = 0; - cfg |= S5P_CIO_OFFS_HOR(offset->cr_h); - cfg |= S5P_CIO_OFFS_VER(offset->cr_v); - writel(cfg, dev->regs + S5P_CIOCROFF); + cfg = (offset->cr_v << 16) | offset->cr_h; + writel(cfg, dev->regs + FIMC_REG_CIOCROFF); fimc_hw_set_out_dma_size(ctx); /* Configure chroma components order. */ - cfg = readl(dev->regs + S5P_CIOCTRL); + cfg = readl(dev->regs + FIMC_REG_CIOCTRL); - cfg &= ~(S5P_CIOCTRL_ORDER2P_MASK | S5P_CIOCTRL_ORDER422_MASK | - S5P_CIOCTRL_YCBCR_PLANE_MASK | S5P_CIOCTRL_RGB16FMT_MASK); + cfg &= ~(FIMC_REG_CIOCTRL_ORDER2P_MASK | + FIMC_REG_CIOCTRL_ORDER422_MASK | + FIMC_REG_CIOCTRL_YCBCR_PLANE_MASK | + FIMC_REG_CIOCTRL_RGB16FMT_MASK); if (fmt->colplanes == 1) cfg |= ctx->out_order_1p; else if (fmt->colplanes == 2) - cfg |= ctx->out_order_2p | S5P_CIOCTRL_YCBCR_2PLANE; + cfg |= ctx->out_order_2p | FIMC_REG_CIOCTRL_YCBCR_2PLANE; else if (fmt->colplanes == 3) - cfg |= S5P_CIOCTRL_YCBCR_3PLANE; + cfg |= FIMC_REG_CIOCTRL_YCBCR_3PLANE; - if (fmt->color == S5P_FIMC_RGB565) - cfg |= S5P_CIOCTRL_RGB565; - else if (fmt->color == S5P_FIMC_RGB555) - cfg |= S5P_CIOCTRL_ARGB1555; - else if (fmt->color == S5P_FIMC_RGB444) - cfg |= S5P_CIOCTRL_ARGB4444; + if (fmt->color == FIMC_FMT_RGB565) + cfg |= FIMC_REG_CIOCTRL_RGB565; + else if (fmt->color == FIMC_FMT_RGB555) + cfg |= FIMC_REG_CIOCTRL_ARGB1555; + else if (fmt->color == FIMC_FMT_RGB444) + cfg |= FIMC_REG_CIOCTRL_ARGB4444; - writel(cfg, dev->regs + S5P_CIOCTRL); + writel(cfg, dev->regs + FIMC_REG_CIOCTRL); } static void fimc_hw_en_autoload(struct fimc_dev *dev, int enable) { - u32 cfg = readl(dev->regs + S5P_ORGISIZE); + u32 cfg = readl(dev->regs + FIMC_REG_ORGISIZE); if (enable) - cfg |= S5P_CIREAL_ISIZE_AUTOLOAD_EN; + cfg |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; else - cfg &= ~S5P_CIREAL_ISIZE_AUTOLOAD_EN; - writel(cfg, dev->regs + S5P_ORGISIZE); + cfg &= ~FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; + writel(cfg, dev->regs + FIMC_REG_ORGISIZE); } void fimc_hw_en_lastirq(struct fimc_dev *dev, int enable) { - u32 cfg = readl(dev->regs + S5P_CIOCTRL); + u32 cfg = readl(dev->regs + FIMC_REG_CIOCTRL); if (enable) - cfg |= S5P_CIOCTRL_LASTIRQ_ENABLE; + cfg |= FIMC_REG_CIOCTRL_LASTIRQ_ENABLE; else - cfg &= ~S5P_CIOCTRL_LASTIRQ_ENABLE; - writel(cfg, dev->regs + S5P_CIOCTRL); + cfg &= ~FIMC_REG_CIOCTRL_LASTIRQ_ENABLE; + writel(cfg, dev->regs + FIMC_REG_CIOCTRL); } void fimc_hw_set_prescaler(struct fimc_ctx *ctx) @@ -245,15 +236,13 @@ void fimc_hw_set_prescaler(struct fimc_ctx *ctx) u32 cfg, shfactor; shfactor = 10 - (sc->hfactor + sc->vfactor); + cfg = shfactor << 28; - cfg = S5P_CISCPRERATIO_SHFACTOR(shfactor); - cfg |= S5P_CISCPRERATIO_HOR(sc->pre_hratio); - cfg |= S5P_CISCPRERATIO_VER(sc->pre_vratio); - writel(cfg, dev->regs + S5P_CISCPRERATIO); + cfg |= (sc->pre_hratio << 16) | sc->pre_vratio; + writel(cfg, dev->regs + FIMC_REG_CISCPRERATIO); - cfg = S5P_CISCPREDST_WIDTH(sc->pre_dst_width); - cfg |= S5P_CISCPREDST_HEIGHT(sc->pre_dst_height); - writel(cfg, dev->regs + S5P_CISCPREDST); + cfg = (sc->pre_dst_width << 16) | sc->pre_dst_height; + writel(cfg, dev->regs + FIMC_REG_CISCPREDST); } static void fimc_hw_set_scaler(struct fimc_ctx *ctx) @@ -263,93 +252,95 @@ static void fimc_hw_set_scaler(struct fimc_ctx *ctx) struct fimc_frame *src_frame = &ctx->s_frame; struct fimc_frame *dst_frame = &ctx->d_frame; - u32 cfg = readl(dev->regs + S5P_CISCCTRL); + u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); - cfg &= ~(S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE | - S5P_CISCCTRL_SCALEUP_H | S5P_CISCCTRL_SCALEUP_V | - S5P_CISCCTRL_SCALERBYPASS | S5P_CISCCTRL_ONE2ONE | - S5P_CISCCTRL_INRGB_FMT_MASK | S5P_CISCCTRL_OUTRGB_FMT_MASK | - S5P_CISCCTRL_INTERLACE | S5P_CISCCTRL_RGB_EXT); + cfg &= ~(FIMC_REG_CISCCTRL_CSCR2Y_WIDE | FIMC_REG_CISCCTRL_CSCY2R_WIDE | + FIMC_REG_CISCCTRL_SCALEUP_H | FIMC_REG_CISCCTRL_SCALEUP_V | + FIMC_REG_CISCCTRL_SCALERBYPASS | FIMC_REG_CISCCTRL_ONE2ONE | + FIMC_REG_CISCCTRL_INRGB_FMT_MASK | FIMC_REG_CISCCTRL_OUTRGB_FMT_MASK | + FIMC_REG_CISCCTRL_INTERLACE | FIMC_REG_CISCCTRL_RGB_EXT); if (!(ctx->flags & FIMC_COLOR_RANGE_NARROW)) - cfg |= (S5P_CISCCTRL_CSCR2Y_WIDE | S5P_CISCCTRL_CSCY2R_WIDE); + cfg |= (FIMC_REG_CISCCTRL_CSCR2Y_WIDE | + FIMC_REG_CISCCTRL_CSCY2R_WIDE); if (!sc->enabled) - cfg |= S5P_CISCCTRL_SCALERBYPASS; + cfg |= FIMC_REG_CISCCTRL_SCALERBYPASS; if (sc->scaleup_h) - cfg |= S5P_CISCCTRL_SCALEUP_H; + cfg |= FIMC_REG_CISCCTRL_SCALEUP_H; if (sc->scaleup_v) - cfg |= S5P_CISCCTRL_SCALEUP_V; + cfg |= FIMC_REG_CISCCTRL_SCALEUP_V; if (sc->copy_mode) - cfg |= S5P_CISCCTRL_ONE2ONE; + cfg |= FIMC_REG_CISCCTRL_ONE2ONE; - if (ctx->in_path == FIMC_DMA) { + if (ctx->in_path == FIMC_IO_DMA) { switch (src_frame->fmt->color) { - case S5P_FIMC_RGB565: - cfg |= S5P_CISCCTRL_INRGB_FMT_RGB565; + case FIMC_FMT_RGB565: + cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB565; break; - case S5P_FIMC_RGB666: - cfg |= S5P_CISCCTRL_INRGB_FMT_RGB666; + case FIMC_FMT_RGB666: + cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB666; break; - case S5P_FIMC_RGB888: - cfg |= S5P_CISCCTRL_INRGB_FMT_RGB888; + case FIMC_FMT_RGB888: + cfg |= FIMC_REG_CISCCTRL_INRGB_FMT_RGB888; break; } } - if (ctx->out_path == FIMC_DMA) { + if (ctx->out_path == FIMC_IO_DMA) { u32 color = dst_frame->fmt->color; - if (color >= S5P_FIMC_RGB444 && color <= S5P_FIMC_RGB565) - cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB565; - else if (color == S5P_FIMC_RGB666) - cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB666; - else if (color == S5P_FIMC_RGB888) - cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888; + if (color >= FIMC_FMT_RGB444 && color <= FIMC_FMT_RGB565) + cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565; + else if (color == FIMC_FMT_RGB666) + cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666; + else if (color == FIMC_FMT_RGB888) + cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888; } else { - cfg |= S5P_CISCCTRL_OUTRGB_FMT_RGB888; + cfg |= FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888; if (ctx->flags & FIMC_SCAN_MODE_INTERLACED) - cfg |= S5P_CISCCTRL_INTERLACE; + cfg |= FIMC_REG_CISCCTRL_INTERLACE; } - writel(cfg, dev->regs + S5P_CISCCTRL); + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); } void fimc_hw_set_mainscaler(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - struct samsung_fimc_variant *variant = dev->variant; + struct fimc_variant *variant = dev->variant; struct fimc_scaler *sc = &ctx->scaler; u32 cfg; dbg("main_hratio= 0x%X main_vratio= 0x%X", - sc->main_hratio, sc->main_vratio); + sc->main_hratio, sc->main_vratio); fimc_hw_set_scaler(ctx); - cfg = readl(dev->regs + S5P_CISCCTRL); - cfg &= ~(S5P_CISCCTRL_MHRATIO_MASK | S5P_CISCCTRL_MVRATIO_MASK); + cfg = readl(dev->regs + FIMC_REG_CISCCTRL); + cfg &= ~(FIMC_REG_CISCCTRL_MHRATIO_MASK | + FIMC_REG_CISCCTRL_MVRATIO_MASK); if (variant->has_mainscaler_ext) { - cfg |= S5P_CISCCTRL_MHRATIO_EXT(sc->main_hratio); - cfg |= S5P_CISCCTRL_MVRATIO_EXT(sc->main_vratio); - writel(cfg, dev->regs + S5P_CISCCTRL); + cfg |= FIMC_REG_CISCCTRL_MHRATIO_EXT(sc->main_hratio); + cfg |= FIMC_REG_CISCCTRL_MVRATIO_EXT(sc->main_vratio); + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); - cfg = readl(dev->regs + S5P_CIEXTEN); + cfg = readl(dev->regs + FIMC_REG_CIEXTEN); - cfg &= ~(S5P_CIEXTEN_MVRATIO_EXT_MASK | - S5P_CIEXTEN_MHRATIO_EXT_MASK); - cfg |= S5P_CIEXTEN_MHRATIO_EXT(sc->main_hratio); - cfg |= S5P_CIEXTEN_MVRATIO_EXT(sc->main_vratio); - writel(cfg, dev->regs + S5P_CIEXTEN); + cfg &= ~(FIMC_REG_CIEXTEN_MVRATIO_EXT_MASK | + FIMC_REG_CIEXTEN_MHRATIO_EXT_MASK); + cfg |= FIMC_REG_CIEXTEN_MHRATIO_EXT(sc->main_hratio); + cfg |= FIMC_REG_CIEXTEN_MVRATIO_EXT(sc->main_vratio); + writel(cfg, dev->regs + FIMC_REG_CIEXTEN); } else { - cfg |= S5P_CISCCTRL_MHRATIO(sc->main_hratio); - cfg |= S5P_CISCCTRL_MVRATIO(sc->main_vratio); - writel(cfg, dev->regs + S5P_CISCCTRL); + cfg |= FIMC_REG_CISCCTRL_MHRATIO(sc->main_hratio); + cfg |= FIMC_REG_CISCCTRL_MVRATIO(sc->main_vratio); + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); } } @@ -357,40 +348,41 @@ void fimc_hw_en_capture(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - u32 cfg = readl(dev->regs + S5P_CIIMGCPT); + u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); - if (ctx->out_path == FIMC_DMA) { + if (ctx->out_path == FIMC_IO_DMA) { /* one shot mode */ - cfg |= S5P_CIIMGCPT_CPT_FREN_ENABLE | S5P_CIIMGCPT_IMGCPTEN; + cfg |= FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | + FIMC_REG_CIIMGCPT_IMGCPTEN; } else { /* Continuous frame capture mode (freerun). */ - cfg &= ~(S5P_CIIMGCPT_CPT_FREN_ENABLE | - S5P_CIIMGCPT_CPT_FRMOD_CNT); - cfg |= S5P_CIIMGCPT_IMGCPTEN; + cfg &= ~(FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE | + FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT); + cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; } if (ctx->scaler.enabled) - cfg |= S5P_CIIMGCPT_IMGCPTEN_SC; + cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN_SC; - writel(cfg | S5P_CIIMGCPT_IMGCPTEN, dev->regs + S5P_CIIMGCPT); + cfg |= FIMC_REG_CIIMGCPT_IMGCPTEN; + writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); } -void fimc_hw_set_effect(struct fimc_ctx *ctx, bool active) +void fimc_hw_set_effect(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; struct fimc_effect *effect = &ctx->effect; u32 cfg = 0; - if (active) { - cfg |= S5P_CIIMGEFF_IE_SC_AFTER | S5P_CIIMGEFF_IE_ENABLE; + if (effect->type != FIMC_REG_CIIMGEFF_FIN_BYPASS) { + cfg |= FIMC_REG_CIIMGEFF_IE_SC_AFTER | + FIMC_REG_CIIMGEFF_IE_ENABLE; cfg |= effect->type; - if (effect->type == S5P_FIMC_EFFECT_ARBITRARY) { - cfg |= S5P_CIIMGEFF_PAT_CB(effect->pat_cb); - cfg |= S5P_CIIMGEFF_PAT_CR(effect->pat_cr); - } + if (effect->type == FIMC_REG_CIIMGEFF_FIN_ARBITRARY) + cfg |= (effect->pat_cb << 13) | effect->pat_cr; } - writel(cfg, dev->regs + S5P_CIIMGEFF); + writel(cfg, dev->regs + FIMC_REG_CIIMGEFF); } void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx) @@ -402,10 +394,10 @@ void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx) if (!(frame->fmt->flags & FMT_HAS_ALPHA)) return; - cfg = readl(dev->regs + S5P_CIOCTRL); - cfg &= ~S5P_CIOCTRL_ALPHA_OUT_MASK; + cfg = readl(dev->regs + FIMC_REG_CIOCTRL); + cfg &= ~FIMC_REG_CIOCTRL_ALPHA_OUT_MASK; cfg |= (frame->alpha << 4); - writel(cfg, dev->regs + S5P_CIOCTRL); + writel(cfg, dev->regs + FIMC_REG_CIOCTRL); } static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx) @@ -415,16 +407,14 @@ static void fimc_hw_set_in_dma_size(struct fimc_ctx *ctx) u32 cfg_o = 0; u32 cfg_r = 0; - if (FIMC_LCDFIFO == ctx->out_path) - cfg_r |= S5P_CIREAL_ISIZE_AUTOLOAD_EN; + if (FIMC_IO_LCDFIFO == ctx->out_path) + cfg_r |= FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN; - cfg_o |= S5P_ORIG_SIZE_HOR(frame->f_width); - cfg_o |= S5P_ORIG_SIZE_VER(frame->f_height); - cfg_r |= S5P_CIREAL_ISIZE_WIDTH(frame->width); - cfg_r |= S5P_CIREAL_ISIZE_HEIGHT(frame->height); + cfg_o |= (frame->f_height << 16) | frame->f_width; + cfg_r |= (frame->height << 16) | frame->width; - writel(cfg_o, dev->regs + S5P_ORGISIZE); - writel(cfg_r, dev->regs + S5P_CIREAL_ISIZE); + writel(cfg_o, dev->regs + FIMC_REG_ORGISIZE); + writel(cfg_r, dev->regs + FIMC_REG_CIREAL_ISIZE); } void fimc_hw_set_in_dma(struct fimc_ctx *ctx) @@ -435,80 +425,77 @@ void fimc_hw_set_in_dma(struct fimc_ctx *ctx) u32 cfg; /* Set the pixel offsets. */ - cfg = S5P_CIO_OFFS_HOR(offset->y_h); - cfg |= S5P_CIO_OFFS_VER(offset->y_v); - writel(cfg, dev->regs + S5P_CIIYOFF); + cfg = (offset->y_v << 16) | offset->y_h; + writel(cfg, dev->regs + FIMC_REG_CIIYOFF); - cfg = S5P_CIO_OFFS_HOR(offset->cb_h); - cfg |= S5P_CIO_OFFS_VER(offset->cb_v); - writel(cfg, dev->regs + S5P_CIICBOFF); + cfg = (offset->cb_v << 16) | offset->cb_h; + writel(cfg, dev->regs + FIMC_REG_CIICBOFF); - cfg = S5P_CIO_OFFS_HOR(offset->cr_h); - cfg |= S5P_CIO_OFFS_VER(offset->cr_v); - writel(cfg, dev->regs + S5P_CIICROFF); + cfg = (offset->cr_v << 16) | offset->cr_h; + writel(cfg, dev->regs + FIMC_REG_CIICROFF); /* Input original and real size. */ fimc_hw_set_in_dma_size(ctx); /* Use DMA autoload only in FIFO mode. */ - fimc_hw_en_autoload(dev, ctx->out_path == FIMC_LCDFIFO); + fimc_hw_en_autoload(dev, ctx->out_path == FIMC_IO_LCDFIFO); /* Set the input DMA to process single frame only. */ - cfg = readl(dev->regs + S5P_MSCTRL); - cfg &= ~(S5P_MSCTRL_INFORMAT_MASK - | S5P_MSCTRL_IN_BURST_COUNT_MASK - | S5P_MSCTRL_INPUT_MASK - | S5P_MSCTRL_C_INT_IN_MASK - | S5P_MSCTRL_2P_IN_ORDER_MASK); + cfg = readl(dev->regs + FIMC_REG_MSCTRL); + cfg &= ~(FIMC_REG_MSCTRL_INFORMAT_MASK + | FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK + | FIMC_REG_MSCTRL_INPUT_MASK + | FIMC_REG_MSCTRL_C_INT_IN_MASK + | FIMC_REG_MSCTRL_2P_IN_ORDER_MASK); - cfg |= (S5P_MSCTRL_IN_BURST_COUNT(4) - | S5P_MSCTRL_INPUT_MEMORY - | S5P_MSCTRL_FIFO_CTRL_FULL); + cfg |= (FIMC_REG_MSCTRL_IN_BURST_COUNT(4) + | FIMC_REG_MSCTRL_INPUT_MEMORY + | FIMC_REG_MSCTRL_FIFO_CTRL_FULL); switch (frame->fmt->color) { - case S5P_FIMC_RGB565...S5P_FIMC_RGB888: - cfg |= S5P_MSCTRL_INFORMAT_RGB; + case FIMC_FMT_RGB565...FIMC_FMT_RGB888: + cfg |= FIMC_REG_MSCTRL_INFORMAT_RGB; break; - case S5P_FIMC_YCBCR420: - cfg |= S5P_MSCTRL_INFORMAT_YCBCR420; + case FIMC_FMT_YCBCR420: + cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR420; if (frame->fmt->colplanes == 2) - cfg |= ctx->in_order_2p | S5P_MSCTRL_C_INT_IN_2PLANE; + cfg |= ctx->in_order_2p | FIMC_REG_MSCTRL_C_INT_IN_2PLANE; else - cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; + cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE; break; - case S5P_FIMC_YCBYCR422...S5P_FIMC_CRYCBY422: + case FIMC_FMT_YCBYCR422...FIMC_FMT_CRYCBY422: if (frame->fmt->colplanes == 1) { cfg |= ctx->in_order_1p - | S5P_MSCTRL_INFORMAT_YCBCR422_1P; + | FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P; } else { - cfg |= S5P_MSCTRL_INFORMAT_YCBCR422; + cfg |= FIMC_REG_MSCTRL_INFORMAT_YCBCR422; if (frame->fmt->colplanes == 2) cfg |= ctx->in_order_2p - | S5P_MSCTRL_C_INT_IN_2PLANE; + | FIMC_REG_MSCTRL_C_INT_IN_2PLANE; else - cfg |= S5P_MSCTRL_C_INT_IN_3PLANE; + cfg |= FIMC_REG_MSCTRL_C_INT_IN_3PLANE; } break; default: break; } - writel(cfg, dev->regs + S5P_MSCTRL); + writel(cfg, dev->regs + FIMC_REG_MSCTRL); /* Input/output DMA linear/tiled mode. */ - cfg = readl(dev->regs + S5P_CIDMAPARAM); - cfg &= ~S5P_CIDMAPARAM_TILE_MASK; + cfg = readl(dev->regs + FIMC_REG_CIDMAPARAM); + cfg &= ~FIMC_REG_CIDMAPARAM_TILE_MASK; if (tiled_fmt(ctx->s_frame.fmt)) - cfg |= S5P_CIDMAPARAM_R_64X32; + cfg |= FIMC_REG_CIDMAPARAM_R_64X32; if (tiled_fmt(ctx->d_frame.fmt)) - cfg |= S5P_CIDMAPARAM_W_64X32; + cfg |= FIMC_REG_CIDMAPARAM_W_64X32; - writel(cfg, dev->regs + S5P_CIDMAPARAM); + writel(cfg, dev->regs + FIMC_REG_CIDMAPARAM); } @@ -516,40 +503,40 @@ void fimc_hw_set_input_path(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - u32 cfg = readl(dev->regs + S5P_MSCTRL); - cfg &= ~S5P_MSCTRL_INPUT_MASK; + u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL); + cfg &= ~FIMC_REG_MSCTRL_INPUT_MASK; - if (ctx->in_path == FIMC_DMA) - cfg |= S5P_MSCTRL_INPUT_MEMORY; + if (ctx->in_path == FIMC_IO_DMA) + cfg |= FIMC_REG_MSCTRL_INPUT_MEMORY; else - cfg |= S5P_MSCTRL_INPUT_EXTCAM; + cfg |= FIMC_REG_MSCTRL_INPUT_EXTCAM; - writel(cfg, dev->regs + S5P_MSCTRL); + writel(cfg, dev->regs + FIMC_REG_MSCTRL); } void fimc_hw_set_output_path(struct fimc_ctx *ctx) { struct fimc_dev *dev = ctx->fimc_dev; - u32 cfg = readl(dev->regs + S5P_CISCCTRL); - cfg &= ~S5P_CISCCTRL_LCDPATHEN_FIFO; - if (ctx->out_path == FIMC_LCDFIFO) - cfg |= S5P_CISCCTRL_LCDPATHEN_FIFO; - writel(cfg, dev->regs + S5P_CISCCTRL); + u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); + cfg &= ~FIMC_REG_CISCCTRL_LCDPATHEN_FIFO; + if (ctx->out_path == FIMC_IO_LCDFIFO) + cfg |= FIMC_REG_CISCCTRL_LCDPATHEN_FIFO; + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); } void fimc_hw_set_input_addr(struct fimc_dev *dev, struct fimc_addr *paddr) { - u32 cfg = readl(dev->regs + S5P_CIREAL_ISIZE); - cfg |= S5P_CIREAL_ISIZE_ADDR_CH_DIS; - writel(cfg, dev->regs + S5P_CIREAL_ISIZE); + u32 cfg = readl(dev->regs + FIMC_REG_CIREAL_ISIZE); + cfg |= FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS; + writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE); - writel(paddr->y, dev->regs + S5P_CIIYSA(0)); - writel(paddr->cb, dev->regs + S5P_CIICBSA(0)); - writel(paddr->cr, dev->regs + S5P_CIICRSA(0)); + writel(paddr->y, dev->regs + FIMC_REG_CIIYSA(0)); + writel(paddr->cb, dev->regs + FIMC_REG_CIICBSA(0)); + writel(paddr->cr, dev->regs + FIMC_REG_CIICRSA(0)); - cfg &= ~S5P_CIREAL_ISIZE_ADDR_CH_DIS; - writel(cfg, dev->regs + S5P_CIREAL_ISIZE); + cfg &= ~FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS; + writel(cfg, dev->regs + FIMC_REG_CIREAL_ISIZE); } void fimc_hw_set_output_addr(struct fimc_dev *dev, @@ -557,9 +544,9 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev, { int i = (index == -1) ? 0 : index; do { - writel(paddr->y, dev->regs + S5P_CIOYSA(i)); - writel(paddr->cb, dev->regs + S5P_CIOCBSA(i)); - writel(paddr->cr, dev->regs + S5P_CIOCRSA(i)); + writel(paddr->y, dev->regs + FIMC_REG_CIOYSA(i)); + writel(paddr->cb, dev->regs + FIMC_REG_CIOCBSA(i)); + writel(paddr->cr, dev->regs + FIMC_REG_CIOCRSA(i)); dbg("dst_buf[%d]: 0x%X, cb: 0x%X, cr: 0x%X", i, paddr->y, paddr->cb, paddr->cr); } while (index == -1 && ++i < FIMC_MAX_OUT_BUFS); @@ -568,32 +555,45 @@ void fimc_hw_set_output_addr(struct fimc_dev *dev, int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, struct s5p_fimc_isp_info *cam) { - u32 cfg = readl(fimc->regs + S5P_CIGCTRL); + u32 cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); - cfg &= ~(S5P_CIGCTRL_INVPOLPCLK | S5P_CIGCTRL_INVPOLVSYNC | - S5P_CIGCTRL_INVPOLHREF | S5P_CIGCTRL_INVPOLHSYNC | - S5P_CIGCTRL_INVPOLFIELD); + cfg &= ~(FIMC_REG_CIGCTRL_INVPOLPCLK | FIMC_REG_CIGCTRL_INVPOLVSYNC | + FIMC_REG_CIGCTRL_INVPOLHREF | FIMC_REG_CIGCTRL_INVPOLHSYNC | + FIMC_REG_CIGCTRL_INVPOLFIELD); if (cam->flags & V4L2_MBUS_PCLK_SAMPLE_FALLING) - cfg |= S5P_CIGCTRL_INVPOLPCLK; + cfg |= FIMC_REG_CIGCTRL_INVPOLPCLK; if (cam->flags & V4L2_MBUS_VSYNC_ACTIVE_LOW) - cfg |= S5P_CIGCTRL_INVPOLVSYNC; + cfg |= FIMC_REG_CIGCTRL_INVPOLVSYNC; if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - cfg |= S5P_CIGCTRL_INVPOLHREF; + cfg |= FIMC_REG_CIGCTRL_INVPOLHREF; if (cam->flags & V4L2_MBUS_HSYNC_ACTIVE_LOW) - cfg |= S5P_CIGCTRL_INVPOLHSYNC; + cfg |= FIMC_REG_CIGCTRL_INVPOLHSYNC; if (cam->flags & V4L2_MBUS_FIELD_EVEN_LOW) - cfg |= S5P_CIGCTRL_INVPOLFIELD; + cfg |= FIMC_REG_CIGCTRL_INVPOLFIELD; - writel(cfg, fimc->regs + S5P_CIGCTRL); + writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); return 0; } +struct mbus_pixfmt_desc { + u32 pixelcode; + u32 cisrcfmt; + u16 bus_width; +}; + +static const struct mbus_pixfmt_desc pix_desc[] = { + { V4L2_MBUS_FMT_YUYV8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCBYCR, 8 }, + { V4L2_MBUS_FMT_YVYU8_2X8, FIMC_REG_CISRCFMT_ORDER422_YCRYCB, 8 }, + { V4L2_MBUS_FMT_VYUY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CRYCBY, 8 }, + { V4L2_MBUS_FMT_UYVY8_2X8, FIMC_REG_CISRCFMT_ORDER422_CBYCRY, 8 }, +}; + int fimc_hw_set_camera_source(struct fimc_dev *fimc, struct s5p_fimc_isp_info *cam) { @@ -602,18 +602,6 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, u32 bus_width; int i; - static const struct { - u32 pixelcode; - u32 cisrcfmt; - u16 bus_width; - } pix_desc[] = { - { V4L2_MBUS_FMT_YUYV8_2X8, S5P_CISRCFMT_ORDER422_YCBYCR, 8 }, - { V4L2_MBUS_FMT_YVYU8_2X8, S5P_CISRCFMT_ORDER422_YCRYCB, 8 }, - { V4L2_MBUS_FMT_VYUY8_2X8, S5P_CISRCFMT_ORDER422_CRYCBY, 8 }, - { V4L2_MBUS_FMT_UYVY8_2X8, S5P_CISRCFMT_ORDER422_CBYCRY, 8 }, - /* TODO: Add pixel codes for 16-bit bus width */ - }; - if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { for (i = 0; i < ARRAY_SIZE(pix_desc); i++) { if (fimc->vid_cap.mf.code == pix_desc[i].pixelcode) { @@ -632,41 +620,37 @@ int fimc_hw_set_camera_source(struct fimc_dev *fimc, if (cam->bus_type == FIMC_ITU_601) { if (bus_width == 8) - cfg |= S5P_CISRCFMT_ITU601_8BIT; + cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; else if (bus_width == 16) - cfg |= S5P_CISRCFMT_ITU601_16BIT; + cfg |= FIMC_REG_CISRCFMT_ITU601_16BIT; } /* else defaults to ITU-R BT.656 8-bit */ } else if (cam->bus_type == FIMC_MIPI_CSI2) { if (fimc_fmt_is_jpeg(f->fmt->color)) - cfg |= S5P_CISRCFMT_ITU601_8BIT; + cfg |= FIMC_REG_CISRCFMT_ITU601_8BIT; } - cfg |= S5P_CISRCFMT_HSIZE(f->o_width) | S5P_CISRCFMT_VSIZE(f->o_height); - writel(cfg, fimc->regs + S5P_CISRCFMT); + cfg |= (f->o_width << 16) | f->o_height; + writel(cfg, fimc->regs + FIMC_REG_CISRCFMT); return 0; } - -int fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) +void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f) { u32 hoff2, voff2; - u32 cfg = readl(fimc->regs + S5P_CIWDOFST); + u32 cfg = readl(fimc->regs + FIMC_REG_CIWDOFST); - cfg &= ~(S5P_CIWDOFST_HOROFF_MASK | S5P_CIWDOFST_VEROFF_MASK); - cfg |= S5P_CIWDOFST_OFF_EN | - S5P_CIWDOFST_HOROFF(f->offs_h) | - S5P_CIWDOFST_VEROFF(f->offs_v); + cfg &= ~(FIMC_REG_CIWDOFST_HOROFF_MASK | FIMC_REG_CIWDOFST_VEROFF_MASK); + cfg |= FIMC_REG_CIWDOFST_OFF_EN | + (f->offs_h << 16) | f->offs_v; - writel(cfg, fimc->regs + S5P_CIWDOFST); + writel(cfg, fimc->regs + FIMC_REG_CIWDOFST); /* See CIWDOFSTn register description in the datasheet for details. */ hoff2 = f->o_width - f->width - f->offs_h; voff2 = f->o_height - f->height - f->offs_v; - cfg = S5P_CIWDOFST2_HOROFF(hoff2) | S5P_CIWDOFST2_VEROFF(voff2); - - writel(cfg, fimc->regs + S5P_CIWDOFST2); - return 0; + cfg = (hoff2 << 16) | voff2; + writel(cfg, fimc->regs + FIMC_REG_CIWDOFST2); } int fimc_hw_set_camera_type(struct fimc_dev *fimc, @@ -674,28 +658,29 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, { u32 cfg, tmp; struct fimc_vid_cap *vid_cap = &fimc->vid_cap; + u32 csis_data_alignment = 32; - cfg = readl(fimc->regs + S5P_CIGCTRL); + cfg = readl(fimc->regs + FIMC_REG_CIGCTRL); /* Select ITU B interface, disable Writeback path and test pattern. */ - cfg &= ~(S5P_CIGCTRL_TESTPAT_MASK | S5P_CIGCTRL_SELCAM_ITU_A | - S5P_CIGCTRL_SELCAM_MIPI | S5P_CIGCTRL_CAMIF_SELWB | - S5P_CIGCTRL_SELCAM_MIPI_A | S5P_CIGCTRL_CAM_JPEG); + cfg &= ~(FIMC_REG_CIGCTRL_TESTPAT_MASK | FIMC_REG_CIGCTRL_SELCAM_ITU_A | + FIMC_REG_CIGCTRL_SELCAM_MIPI | FIMC_REG_CIGCTRL_CAMIF_SELWB | + FIMC_REG_CIGCTRL_SELCAM_MIPI_A | FIMC_REG_CIGCTRL_CAM_JPEG); if (cam->bus_type == FIMC_MIPI_CSI2) { - cfg |= S5P_CIGCTRL_SELCAM_MIPI; + cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI; if (cam->mux_id == 0) - cfg |= S5P_CIGCTRL_SELCAM_MIPI_A; + cfg |= FIMC_REG_CIGCTRL_SELCAM_MIPI_A; /* TODO: add remaining supported formats. */ switch (vid_cap->mf.code) { case V4L2_MBUS_FMT_VYUY8_2X8: - tmp = S5P_CSIIMGFMT_YCBCR422_8BIT; + tmp = FIMC_REG_CSIIMGFMT_YCBCR422_8BIT; break; case V4L2_MBUS_FMT_JPEG_1X8: - tmp = S5P_CSIIMGFMT_USER(1); - cfg |= S5P_CIGCTRL_CAM_JPEG; + tmp = FIMC_REG_CSIIMGFMT_USER(1); + cfg |= FIMC_REG_CIGCTRL_CAM_JPEG; break; default: v4l2_err(fimc->vid_cap.vfd, @@ -703,21 +688,86 @@ int fimc_hw_set_camera_type(struct fimc_dev *fimc, vid_cap->mf.code); return -EINVAL; } - tmp |= (cam->csi_data_align == 32) << 8; + tmp |= (csis_data_alignment == 32) << 8; - writel(tmp, fimc->regs + S5P_CSIIMGFMT); + writel(tmp, fimc->regs + FIMC_REG_CSIIMGFMT); } else if (cam->bus_type == FIMC_ITU_601 || cam->bus_type == FIMC_ITU_656) { if (cam->mux_id == 0) /* ITU-A, ITU-B: 0, 1 */ - cfg |= S5P_CIGCTRL_SELCAM_ITU_A; + cfg |= FIMC_REG_CIGCTRL_SELCAM_ITU_A; } else if (cam->bus_type == FIMC_LCD_WB) { - cfg |= S5P_CIGCTRL_CAMIF_SELWB; + cfg |= FIMC_REG_CIGCTRL_CAMIF_SELWB; } else { err("invalid camera bus type selected\n"); return -EINVAL; } - writel(cfg, fimc->regs + S5P_CIGCTRL); + writel(cfg, fimc->regs + FIMC_REG_CIGCTRL); return 0; } + +void fimc_hw_clear_irq(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CIGCTRL); + cfg |= FIMC_REG_CIGCTRL_IRQ_CLR; + writel(cfg, dev->regs + FIMC_REG_CIGCTRL); +} + +void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CISCCTRL); + if (on) + cfg |= FIMC_REG_CISCCTRL_SCALERSTART; + else + cfg &= ~FIMC_REG_CISCCTRL_SCALERSTART; + writel(cfg, dev->regs + FIMC_REG_CISCCTRL); +} + +void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on) +{ + u32 cfg = readl(dev->regs + FIMC_REG_MSCTRL); + if (on) + cfg |= FIMC_REG_MSCTRL_ENVID; + else + cfg &= ~FIMC_REG_MSCTRL_ENVID; + writel(cfg, dev->regs + FIMC_REG_MSCTRL); +} + +void fimc_hw_dis_capture(struct fimc_dev *dev) +{ + u32 cfg = readl(dev->regs + FIMC_REG_CIIMGCPT); + cfg &= ~(FIMC_REG_CIIMGCPT_IMGCPTEN | FIMC_REG_CIIMGCPT_IMGCPTEN_SC); + writel(cfg, dev->regs + FIMC_REG_CIIMGCPT); +} + +/* Return an index to the buffer actually being written. */ +u32 fimc_hw_get_frame_index(struct fimc_dev *dev) +{ + u32 reg; + + if (dev->variant->has_cistatus2) { + reg = readl(dev->regs + FIMC_REG_CISTATUS2) & 0x3F; + return reg > 0 ? --reg : reg; + } + + reg = readl(dev->regs + FIMC_REG_CISTATUS); + + return (reg & FIMC_REG_CISTATUS_FRAMECNT_MASK) >> + FIMC_REG_CISTATUS_FRAMECNT_SHIFT; +} + +/* Locking: the caller holds fimc->slock */ +void fimc_activate_capture(struct fimc_ctx *ctx) +{ + fimc_hw_enable_scaler(ctx->fimc_dev, ctx->scaler.enabled); + fimc_hw_en_capture(ctx); +} + +void fimc_deactivate_capture(struct fimc_dev *fimc) +{ + fimc_hw_en_lastirq(fimc, true); + fimc_hw_dis_capture(fimc); + fimc_hw_enable_scaler(fimc, false); + fimc_hw_en_lastirq(fimc, false); +} diff --git a/drivers/media/video/s5p-fimc/fimc-reg.h b/drivers/media/video/s5p-fimc/fimc-reg.h new file mode 100644 index 000000000000..579ac8ac03de --- /dev/null +++ b/drivers/media/video/s5p-fimc/fimc-reg.h @@ -0,0 +1,326 @@ +/* + * Samsung camera host interface (FIMC) registers definition + * + * Copyright (C) 2010 - 2012 Samsung Electronics Co., Ltd. + * + * 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 FIMC_REG_H_ +#define FIMC_REG_H_ + +#include "fimc-core.h" + +/* Input source format */ +#define FIMC_REG_CISRCFMT 0x00 +#define FIMC_REG_CISRCFMT_ITU601_8BIT (1 << 31) +#define FIMC_REG_CISRCFMT_ITU601_16BIT (1 << 29) +#define FIMC_REG_CISRCFMT_ORDER422_YCBYCR (0 << 14) +#define FIMC_REG_CISRCFMT_ORDER422_YCRYCB (1 << 14) +#define FIMC_REG_CISRCFMT_ORDER422_CBYCRY (2 << 14) +#define FIMC_REG_CISRCFMT_ORDER422_CRYCBY (3 << 14) + +/* Window offset */ +#define FIMC_REG_CIWDOFST 0x04 +#define FIMC_REG_CIWDOFST_OFF_EN (1 << 31) +#define FIMC_REG_CIWDOFST_CLROVFIY (1 << 30) +#define FIMC_REG_CIWDOFST_CLROVRLB (1 << 29) +#define FIMC_REG_CIWDOFST_HOROFF_MASK (0x7ff << 16) +#define FIMC_REG_CIWDOFST_CLROVFICB (1 << 15) +#define FIMC_REG_CIWDOFST_CLROVFICR (1 << 14) +#define FIMC_REG_CIWDOFST_VEROFF_MASK (0xfff << 0) + +/* Global control */ +#define FIMC_REG_CIGCTRL 0x08 +#define FIMC_REG_CIGCTRL_SWRST (1 << 31) +#define FIMC_REG_CIGCTRL_CAMRST_A (1 << 30) +#define FIMC_REG_CIGCTRL_SELCAM_ITU_A (1 << 29) +#define FIMC_REG_CIGCTRL_TESTPAT_NORMAL (0 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_HOR_INC (2 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_VER_INC (3 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_MASK (3 << 27) +#define FIMC_REG_CIGCTRL_TESTPAT_SHIFT 27 +#define FIMC_REG_CIGCTRL_INVPOLPCLK (1 << 26) +#define FIMC_REG_CIGCTRL_INVPOLVSYNC (1 << 25) +#define FIMC_REG_CIGCTRL_INVPOLHREF (1 << 24) +#define FIMC_REG_CIGCTRL_IRQ_OVFEN (1 << 22) +#define FIMC_REG_CIGCTRL_HREF_MASK (1 << 21) +#define FIMC_REG_CIGCTRL_IRQ_LEVEL (1 << 20) +#define FIMC_REG_CIGCTRL_IRQ_CLR (1 << 19) +#define FIMC_REG_CIGCTRL_IRQ_ENABLE (1 << 16) +#define FIMC_REG_CIGCTRL_SHDW_DISABLE (1 << 12) +#define FIMC_REG_CIGCTRL_CAM_JPEG (1 << 8) +#define FIMC_REG_CIGCTRL_SELCAM_MIPI_A (1 << 7) +#define FIMC_REG_CIGCTRL_CAMIF_SELWB (1 << 6) +/* 0 - ITU601; 1 - ITU709 */ +#define FIMC_REG_CIGCTRL_CSC_ITU601_709 (1 << 5) +#define FIMC_REG_CIGCTRL_INVPOLHSYNC (1 << 4) +#define FIMC_REG_CIGCTRL_SELCAM_MIPI (1 << 3) +#define FIMC_REG_CIGCTRL_INVPOLFIELD (1 << 1) +#define FIMC_REG_CIGCTRL_INTERLACE (1 << 0) + +/* Window offset 2 */ +#define FIMC_REG_CIWDOFST2 0x14 +#define FIMC_REG_CIWDOFST2_HOROFF_MASK (0xfff << 16) +#define FIMC_REG_CIWDOFST2_VEROFF_MASK (0xfff << 0) + +/* Output DMA Y/Cb/Cr plane start addresses */ +#define FIMC_REG_CIOYSA(n) (0x18 + (n) * 4) +#define FIMC_REG_CIOCBSA(n) (0x28 + (n) * 4) +#define FIMC_REG_CIOCRSA(n) (0x38 + (n) * 4) + +/* Target image format */ +#define FIMC_REG_CITRGFMT 0x48 +#define FIMC_REG_CITRGFMT_INROT90 (1 << 31) +#define FIMC_REG_CITRGFMT_YCBCR420 (0 << 29) +#define FIMC_REG_CITRGFMT_YCBCR422 (1 << 29) +#define FIMC_REG_CITRGFMT_YCBCR422_1P (2 << 29) +#define FIMC_REG_CITRGFMT_RGB (3 << 29) +#define FIMC_REG_CITRGFMT_FMT_MASK (3 << 29) +#define FIMC_REG_CITRGFMT_HSIZE_MASK (0xfff << 16) +#define FIMC_REG_CITRGFMT_FLIP_SHIFT 14 +#define FIMC_REG_CITRGFMT_FLIP_NORMAL (0 << 14) +#define FIMC_REG_CITRGFMT_FLIP_X_MIRROR (1 << 14) +#define FIMC_REG_CITRGFMT_FLIP_Y_MIRROR (2 << 14) +#define FIMC_REG_CITRGFMT_FLIP_180 (3 << 14) +#define FIMC_REG_CITRGFMT_FLIP_MASK (3 << 14) +#define FIMC_REG_CITRGFMT_OUTROT90 (1 << 13) +#define FIMC_REG_CITRGFMT_VSIZE_MASK (0xfff << 0) + +/* Output DMA control */ +#define FIMC_REG_CIOCTRL 0x4c +#define FIMC_REG_CIOCTRL_ORDER422_MASK (3 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_CRYCBY (0 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_CBYCRY (1 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_YCRYCB (2 << 0) +#define FIMC_REG_CIOCTRL_ORDER422_YCBYCR (3 << 0) +#define FIMC_REG_CIOCTRL_LASTIRQ_ENABLE (1 << 2) +#define FIMC_REG_CIOCTRL_YCBCR_3PLANE (0 << 3) +#define FIMC_REG_CIOCTRL_YCBCR_2PLANE (1 << 3) +#define FIMC_REG_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) +#define FIMC_REG_CIOCTRL_ALPHA_OUT_MASK (0xff << 4) +#define FIMC_REG_CIOCTRL_RGB16FMT_MASK (3 << 16) +#define FIMC_REG_CIOCTRL_RGB565 (0 << 16) +#define FIMC_REG_CIOCTRL_ARGB1555 (1 << 16) +#define FIMC_REG_CIOCTRL_ARGB4444 (2 << 16) +#define FIMC_REG_CIOCTRL_ORDER2P_SHIFT 24 +#define FIMC_REG_CIOCTRL_ORDER2P_MASK (3 << 24) +#define FIMC_REG_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24) + +/* Pre-scaler control 1 */ +#define FIMC_REG_CISCPRERATIO 0x50 + +#define FIMC_REG_CISCPREDST 0x54 + +/* Main scaler control */ +#define FIMC_REG_CISCCTRL 0x58 +#define FIMC_REG_CISCCTRL_SCALERBYPASS (1 << 31) +#define FIMC_REG_CISCCTRL_SCALEUP_H (1 << 30) +#define FIMC_REG_CISCCTRL_SCALEUP_V (1 << 29) +#define FIMC_REG_CISCCTRL_CSCR2Y_WIDE (1 << 28) +#define FIMC_REG_CISCCTRL_CSCY2R_WIDE (1 << 27) +#define FIMC_REG_CISCCTRL_LCDPATHEN_FIFO (1 << 26) +#define FIMC_REG_CISCCTRL_INTERLACE (1 << 25) +#define FIMC_REG_CISCCTRL_SCALERSTART (1 << 15) +#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) +#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) +#define FIMC_REG_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) +#define FIMC_REG_CISCCTRL_INRGB_FMT_MASK (3 << 13) +#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) +#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) +#define FIMC_REG_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) +#define FIMC_REG_CISCCTRL_OUTRGB_FMT_MASK (3 << 11) +#define FIMC_REG_CISCCTRL_RGB_EXT (1 << 10) +#define FIMC_REG_CISCCTRL_ONE2ONE (1 << 9) +#define FIMC_REG_CISCCTRL_MHRATIO(x) ((x) << 16) +#define FIMC_REG_CISCCTRL_MVRATIO(x) ((x) << 0) +#define FIMC_REG_CISCCTRL_MHRATIO_MASK (0x1ff << 16) +#define FIMC_REG_CISCCTRL_MVRATIO_MASK (0x1ff << 0) +#define FIMC_REG_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16) +#define FIMC_REG_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0) + +/* Target area */ +#define FIMC_REG_CITAREA 0x5c +#define FIMC_REG_CITAREA_MASK 0x0fffffff + +/* General status */ +#define FIMC_REG_CISTATUS 0x64 +#define FIMC_REG_CISTATUS_OVFIY (1 << 31) +#define FIMC_REG_CISTATUS_OVFICB (1 << 30) +#define FIMC_REG_CISTATUS_OVFICR (1 << 29) +#define FIMC_REG_CISTATUS_VSYNC (1 << 28) +#define FIMC_REG_CISTATUS_FRAMECNT_MASK (3 << 26) +#define FIMC_REG_CISTATUS_FRAMECNT_SHIFT 26 +#define FIMC_REG_CISTATUS_WINOFF_EN (1 << 25) +#define FIMC_REG_CISTATUS_IMGCPT_EN (1 << 22) +#define FIMC_REG_CISTATUS_IMGCPT_SCEN (1 << 21) +#define FIMC_REG_CISTATUS_VSYNC_A (1 << 20) +#define FIMC_REG_CISTATUS_VSYNC_B (1 << 19) +#define FIMC_REG_CISTATUS_OVRLB (1 << 18) +#define FIMC_REG_CISTATUS_FRAME_END (1 << 17) +#define FIMC_REG_CISTATUS_LASTCAPT_END (1 << 16) +#define FIMC_REG_CISTATUS_VVALID_A (1 << 15) +#define FIMC_REG_CISTATUS_VVALID_B (1 << 14) + +/* Indexes to the last and the currently processed buffer. */ +#define FIMC_REG_CISTATUS2 0x68 + +/* Image capture control */ +#define FIMC_REG_CIIMGCPT 0xc0 +#define FIMC_REG_CIIMGCPT_IMGCPTEN (1 << 31) +#define FIMC_REG_CIIMGCPT_IMGCPTEN_SC (1 << 30) +#define FIMC_REG_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) +#define FIMC_REG_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) + +/* Frame capture sequence */ +#define FIMC_REG_CICPTSEQ 0xc4 + +/* Image effect */ +#define FIMC_REG_CIIMGEFF 0xd0 +#define FIMC_REG_CIIMGEFF_IE_ENABLE (1 << 30) +#define FIMC_REG_CIIMGEFF_IE_SC_BEFORE (0 << 29) +#define FIMC_REG_CIIMGEFF_IE_SC_AFTER (1 << 29) +#define FIMC_REG_CIIMGEFF_FIN_BYPASS (0 << 26) +#define FIMC_REG_CIIMGEFF_FIN_ARBITRARY (1 << 26) +#define FIMC_REG_CIIMGEFF_FIN_NEGATIVE (2 << 26) +#define FIMC_REG_CIIMGEFF_FIN_ARTFREEZE (3 << 26) +#define FIMC_REG_CIIMGEFF_FIN_EMBOSSING (4 << 26) +#define FIMC_REG_CIIMGEFF_FIN_SILHOUETTE (5 << 26) +#define FIMC_REG_CIIMGEFF_FIN_MASK (7 << 26) +#define FIMC_REG_CIIMGEFF_PAT_CBCR_MASK ((0xff << 13) | 0xff) + +/* Input DMA Y/Cb/Cr plane start address 0/1 */ +#define FIMC_REG_CIIYSA(n) (0xd4 + (n) * 0x70) +#define FIMC_REG_CIICBSA(n) (0xd8 + (n) * 0x70) +#define FIMC_REG_CIICRSA(n) (0xdc + (n) * 0x70) + +/* Real input DMA image size */ +#define FIMC_REG_CIREAL_ISIZE 0xf8 +#define FIMC_REG_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31) +#define FIMC_REG_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30) + +/* Input DMA control */ +#define FIMC_REG_MSCTRL 0xfc +#define FIMC_REG_MSCTRL_IN_BURST_COUNT_MASK (0xf << 24) +#define FIMC_REG_MSCTRL_2P_IN_ORDER_MASK (3 << 16) +#define FIMC_REG_MSCTRL_2P_IN_ORDER_SHIFT 16 +#define FIMC_REG_MSCTRL_C_INT_IN_3PLANE (0 << 15) +#define FIMC_REG_MSCTRL_C_INT_IN_2PLANE (1 << 15) +#define FIMC_REG_MSCTRL_C_INT_IN_MASK (1 << 15) +#define FIMC_REG_MSCTRL_FLIP_SHIFT 13 +#define FIMC_REG_MSCTRL_FLIP_MASK (3 << 13) +#define FIMC_REG_MSCTRL_FLIP_NORMAL (0 << 13) +#define FIMC_REG_MSCTRL_FLIP_X_MIRROR (1 << 13) +#define FIMC_REG_MSCTRL_FLIP_Y_MIRROR (2 << 13) +#define FIMC_REG_MSCTRL_FLIP_180 (3 << 13) +#define FIMC_REG_MSCTRL_FIFO_CTRL_FULL (1 << 12) +#define FIMC_REG_MSCTRL_ORDER422_SHIFT 4 +#define FIMC_REG_MSCTRL_ORDER422_YCBYCR (0 << 4) +#define FIMC_REG_MSCTRL_ORDER422_CBYCRY (1 << 4) +#define FIMC_REG_MSCTRL_ORDER422_YCRYCB (2 << 4) +#define FIMC_REG_MSCTRL_ORDER422_CRYCBY (3 << 4) +#define FIMC_REG_MSCTRL_ORDER422_MASK (3 << 4) +#define FIMC_REG_MSCTRL_INPUT_EXTCAM (0 << 3) +#define FIMC_REG_MSCTRL_INPUT_MEMORY (1 << 3) +#define FIMC_REG_MSCTRL_INPUT_MASK (1 << 3) +#define FIMC_REG_MSCTRL_INFORMAT_YCBCR420 (0 << 1) +#define FIMC_REG_MSCTRL_INFORMAT_YCBCR422 (1 << 1) +#define FIMC_REG_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1) +#define FIMC_REG_MSCTRL_INFORMAT_RGB (3 << 1) +#define FIMC_REG_MSCTRL_INFORMAT_MASK (3 << 1) +#define FIMC_REG_MSCTRL_ENVID (1 << 0) +#define FIMC_REG_MSCTRL_IN_BURST_COUNT(x) ((x) << 24) + +/* Output DMA Y/Cb/Cr offset */ +#define FIMC_REG_CIOYOFF 0x168 +#define FIMC_REG_CIOCBOFF 0x16c +#define FIMC_REG_CIOCROFF 0x170 + +/* Input DMA Y/Cb/Cr offset */ +#define FIMC_REG_CIIYOFF 0x174 +#define FIMC_REG_CIICBOFF 0x178 +#define FIMC_REG_CIICROFF 0x17c + +/* Input DMA original image size */ +#define FIMC_REG_ORGISIZE 0x180 + +/* Output DMA original image size */ +#define FIMC_REG_ORGOSIZE 0x184 + +/* Real output DMA image size (extension register) */ +#define FIMC_REG_CIEXTEN 0x188 +#define FIMC_REG_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10) +#define FIMC_REG_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f) +#define FIMC_REG_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10) +#define FIMC_REG_CIEXTEN_MVRATIO_EXT_MASK 0x3f + +#define FIMC_REG_CIDMAPARAM 0x18c +#define FIMC_REG_CIDMAPARAM_R_LINEAR (0 << 29) +#define FIMC_REG_CIDMAPARAM_R_64X32 (3 << 29) +#define FIMC_REG_CIDMAPARAM_W_LINEAR (0 << 13) +#define FIMC_REG_CIDMAPARAM_W_64X32 (3 << 13) +#define FIMC_REG_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13)) + +/* MIPI CSI image format */ +#define FIMC_REG_CSIIMGFMT 0x194 +#define FIMC_REG_CSIIMGFMT_YCBCR422_8BIT 0x1e +#define FIMC_REG_CSIIMGFMT_RAW8 0x2a +#define FIMC_REG_CSIIMGFMT_RAW10 0x2b +#define FIMC_REG_CSIIMGFMT_RAW12 0x2c +/* User defined formats. x = 0...16. */ +#define FIMC_REG_CSIIMGFMT_USER(x) (0x30 + x - 1) + +/* Output frame buffer sequence mask */ +#define FIMC_REG_CIFCNTSEQ 0x1fc + +/* + * Function declarations + */ +void fimc_hw_reset(struct fimc_dev *fimc); +void fimc_hw_set_rotation(struct fimc_ctx *ctx); +void fimc_hw_set_target_format(struct fimc_ctx *ctx); +void fimc_hw_set_out_dma(struct fimc_ctx *ctx); +void fimc_hw_en_lastirq(struct fimc_dev *fimc, int enable); +void fimc_hw_en_irq(struct fimc_dev *fimc, int enable); +void fimc_hw_set_prescaler(struct fimc_ctx *ctx); +void fimc_hw_set_mainscaler(struct fimc_ctx *ctx); +void fimc_hw_en_capture(struct fimc_ctx *ctx); +void fimc_hw_set_effect(struct fimc_ctx *ctx); +void fimc_hw_set_rgb_alpha(struct fimc_ctx *ctx); +void fimc_hw_set_in_dma(struct fimc_ctx *ctx); +void fimc_hw_set_input_path(struct fimc_ctx *ctx); +void fimc_hw_set_output_path(struct fimc_ctx *ctx); +void fimc_hw_set_input_addr(struct fimc_dev *fimc, struct fimc_addr *paddr); +void fimc_hw_set_output_addr(struct fimc_dev *fimc, struct fimc_addr *paddr, + int index); +int fimc_hw_set_camera_source(struct fimc_dev *fimc, + struct s5p_fimc_isp_info *cam); +void fimc_hw_set_camera_offset(struct fimc_dev *fimc, struct fimc_frame *f); +int fimc_hw_set_camera_polarity(struct fimc_dev *fimc, + struct s5p_fimc_isp_info *cam); +int fimc_hw_set_camera_type(struct fimc_dev *fimc, + struct s5p_fimc_isp_info *cam); +void fimc_hw_clear_irq(struct fimc_dev *dev); +void fimc_hw_enable_scaler(struct fimc_dev *dev, bool on); +void fimc_hw_activate_input_dma(struct fimc_dev *dev, bool on); +void fimc_hw_dis_capture(struct fimc_dev *dev); +u32 fimc_hw_get_frame_index(struct fimc_dev *dev); +void fimc_activate_capture(struct fimc_ctx *ctx); +void fimc_deactivate_capture(struct fimc_dev *fimc); + +/** + * fimc_hw_set_dma_seq - configure output DMA buffer sequence + * @mask: bitmask for the DMA output buffer registers, set to 0 to skip buffer + * This function masks output DMA ring buffers, it allows to select which of + * the 32 available output buffer address registers will be used by the DMA + * engine. + */ +static inline void fimc_hw_set_dma_seq(struct fimc_dev *dev, u32 mask) +{ + writel(mask, dev->regs + FIMC_REG_CIFCNTSEQ); +} + +#endif /* FIMC_REG_H_ */ diff --git a/drivers/media/video/s5p-fimc/mipi-csis.c b/drivers/media/video/s5p-fimc/mipi-csis.c index f44f690397f7..2f73d9e3d0b7 100644 --- a/drivers/media/video/s5p-fimc/mipi-csis.c +++ b/drivers/media/video/s5p-fimc/mipi-csis.c @@ -127,20 +127,24 @@ struct csis_state { * multiple of 2^pix_width_alignment * @code: corresponding media bus code * @fmt_reg: S5PCSIS_CONFIG register value + * @data_alignment: MIPI-CSI data alignment in bits */ struct csis_pix_format { unsigned int pix_width_alignment; enum v4l2_mbus_pixelcode code; u32 fmt_reg; + u8 data_alignment; }; static const struct csis_pix_format s5pcsis_formats[] = { { .code = V4L2_MBUS_FMT_VYUY8_2X8, .fmt_reg = S5PCSIS_CFG_FMT_YCBCR422_8BIT, + .data_alignment = 32, }, { .code = V4L2_MBUS_FMT_JPEG_1X8, .fmt_reg = S5PCSIS_CFG_FMT_USER(1), + .data_alignment = 32, }, }; @@ -239,7 +243,7 @@ static void s5pcsis_set_params(struct csis_state *state) s5pcsis_set_hsync_settle(state, pdata->hs_settle); val = s5pcsis_read(state, S5PCSIS_CTRL); - if (pdata->alignment == 32) + if (state->csis_fmt->data_alignment == 32) val |= S5PCSIS_CTRL_ALIGN_32BIT; else /* 24-bits */ val &= ~S5PCSIS_CTRL_ALIGN_32BIT; @@ -711,19 +715,8 @@ static struct platform_driver s5pcsis_driver = { }, }; -static int __init s5pcsis_init(void) -{ - return platform_driver_probe(&s5pcsis_driver, s5pcsis_probe); -} - -static void __exit s5pcsis_exit(void) -{ - platform_driver_unregister(&s5pcsis_driver); -} - -module_init(s5pcsis_init); -module_exit(s5pcsis_exit); +module_platform_driver(s5pcsis_driver); MODULE_AUTHOR("Sylwester Nawrocki "); -MODULE_DESCRIPTION("S5P/EXYNOS4 MIPI CSI receiver driver"); +MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI-CSI2 receiver driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/s5p-fimc/regs-fimc.h b/drivers/media/video/s5p-fimc/regs-fimc.h deleted file mode 100644 index c7a5bc51d571..000000000000 --- a/drivers/media/video/s5p-fimc/regs-fimc.h +++ /dev/null @@ -1,301 +0,0 @@ -/* - * Register definition file for Samsung Camera Interface (FIMC) driver - * - * Copyright (c) 2010 Samsung Electronics - * - * 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 REGS_FIMC_H_ -#define REGS_FIMC_H_ - -/* Input source format */ -#define S5P_CISRCFMT 0x00 -#define S5P_CISRCFMT_ITU601_8BIT (1 << 31) -#define S5P_CISRCFMT_ITU601_16BIT (1 << 29) -#define S5P_CISRCFMT_ORDER422_YCBYCR (0 << 14) -#define S5P_CISRCFMT_ORDER422_YCRYCB (1 << 14) -#define S5P_CISRCFMT_ORDER422_CBYCRY (2 << 14) -#define S5P_CISRCFMT_ORDER422_CRYCBY (3 << 14) -#define S5P_CISRCFMT_HSIZE(x) ((x) << 16) -#define S5P_CISRCFMT_VSIZE(x) ((x) << 0) - -/* Window offset */ -#define S5P_CIWDOFST 0x04 -#define S5P_CIWDOFST_OFF_EN (1 << 31) -#define S5P_CIWDOFST_CLROVFIY (1 << 30) -#define S5P_CIWDOFST_CLROVRLB (1 << 29) -#define S5P_CIWDOFST_HOROFF_MASK (0x7ff << 16) -#define S5P_CIWDOFST_CLROVFICB (1 << 15) -#define S5P_CIWDOFST_CLROVFICR (1 << 14) -#define S5P_CIWDOFST_HOROFF(x) ((x) << 16) -#define S5P_CIWDOFST_VEROFF(x) ((x) << 0) -#define S5P_CIWDOFST_VEROFF_MASK (0xfff << 0) - -/* Global control */ -#define S5P_CIGCTRL 0x08 -#define S5P_CIGCTRL_SWRST (1 << 31) -#define S5P_CIGCTRL_CAMRST_A (1 << 30) -#define S5P_CIGCTRL_SELCAM_ITU_A (1 << 29) -#define S5P_CIGCTRL_TESTPAT_NORMAL (0 << 27) -#define S5P_CIGCTRL_TESTPAT_COLOR_BAR (1 << 27) -#define S5P_CIGCTRL_TESTPAT_HOR_INC (2 << 27) -#define S5P_CIGCTRL_TESTPAT_VER_INC (3 << 27) -#define S5P_CIGCTRL_TESTPAT_MASK (3 << 27) -#define S5P_CIGCTRL_TESTPAT_SHIFT (27) -#define S5P_CIGCTRL_INVPOLPCLK (1 << 26) -#define S5P_CIGCTRL_INVPOLVSYNC (1 << 25) -#define S5P_CIGCTRL_INVPOLHREF (1 << 24) -#define S5P_CIGCTRL_IRQ_OVFEN (1 << 22) -#define S5P_CIGCTRL_HREF_MASK (1 << 21) -#define S5P_CIGCTRL_IRQ_LEVEL (1 << 20) -#define S5P_CIGCTRL_IRQ_CLR (1 << 19) -#define S5P_CIGCTRL_IRQ_ENABLE (1 << 16) -#define S5P_CIGCTRL_SHDW_DISABLE (1 << 12) -#define S5P_CIGCTRL_CAM_JPEG (1 << 8) -#define S5P_CIGCTRL_SELCAM_MIPI_A (1 << 7) -#define S5P_CIGCTRL_CAMIF_SELWB (1 << 6) -/* 0 - ITU601; 1 - ITU709 */ -#define S5P_CIGCTRL_CSC_ITU601_709 (1 << 5) -#define S5P_CIGCTRL_INVPOLHSYNC (1 << 4) -#define S5P_CIGCTRL_SELCAM_MIPI (1 << 3) -#define S5P_CIGCTRL_INVPOLFIELD (1 << 1) -#define S5P_CIGCTRL_INTERLACE (1 << 0) - -/* Window offset 2 */ -#define S5P_CIWDOFST2 0x14 -#define S5P_CIWDOFST2_HOROFF_MASK (0xfff << 16) -#define S5P_CIWDOFST2_VEROFF_MASK (0xfff << 0) -#define S5P_CIWDOFST2_HOROFF(x) ((x) << 16) -#define S5P_CIWDOFST2_VEROFF(x) ((x) << 0) - -/* Output DMA Y/Cb/Cr plane start addresses */ -#define S5P_CIOYSA(n) (0x18 + (n) * 4) -#define S5P_CIOCBSA(n) (0x28 + (n) * 4) -#define S5P_CIOCRSA(n) (0x38 + (n) * 4) - -/* Target image format */ -#define S5P_CITRGFMT 0x48 -#define S5P_CITRGFMT_INROT90 (1 << 31) -#define S5P_CITRGFMT_YCBCR420 (0 << 29) -#define S5P_CITRGFMT_YCBCR422 (1 << 29) -#define S5P_CITRGFMT_YCBCR422_1P (2 << 29) -#define S5P_CITRGFMT_RGB (3 << 29) -#define S5P_CITRGFMT_FMT_MASK (3 << 29) -#define S5P_CITRGFMT_HSIZE_MASK (0xfff << 16) -#define S5P_CITRGFMT_FLIP_SHIFT (14) -#define S5P_CITRGFMT_FLIP_NORMAL (0 << 14) -#define S5P_CITRGFMT_FLIP_X_MIRROR (1 << 14) -#define S5P_CITRGFMT_FLIP_Y_MIRROR (2 << 14) -#define S5P_CITRGFMT_FLIP_180 (3 << 14) -#define S5P_CITRGFMT_FLIP_MASK (3 << 14) -#define S5P_CITRGFMT_OUTROT90 (1 << 13) -#define S5P_CITRGFMT_VSIZE_MASK (0xfff << 0) -#define S5P_CITRGFMT_HSIZE(x) ((x) << 16) -#define S5P_CITRGFMT_VSIZE(x) ((x) << 0) - -/* Output DMA control */ -#define S5P_CIOCTRL 0x4c -#define S5P_CIOCTRL_ORDER422_MASK (3 << 0) -#define S5P_CIOCTRL_ORDER422_CRYCBY (0 << 0) -#define S5P_CIOCTRL_ORDER422_CBYCRY (1 << 0) -#define S5P_CIOCTRL_ORDER422_YCRYCB (2 << 0) -#define S5P_CIOCTRL_ORDER422_YCBYCR (3 << 0) -#define S5P_CIOCTRL_LASTIRQ_ENABLE (1 << 2) -#define S5P_CIOCTRL_YCBCR_3PLANE (0 << 3) -#define S5P_CIOCTRL_YCBCR_2PLANE (1 << 3) -#define S5P_CIOCTRL_YCBCR_PLANE_MASK (1 << 3) -#define S5P_CIOCTRL_ALPHA_OUT_MASK (0xff << 4) -#define S5P_CIOCTRL_RGB16FMT_MASK (3 << 16) -#define S5P_CIOCTRL_RGB565 (0 << 16) -#define S5P_CIOCTRL_ARGB1555 (1 << 16) -#define S5P_CIOCTRL_ARGB4444 (2 << 16) -#define S5P_CIOCTRL_ORDER2P_SHIFT (24) -#define S5P_CIOCTRL_ORDER2P_MASK (3 << 24) -#define S5P_CIOCTRL_ORDER422_2P_LSB_CRCB (0 << 24) - -/* Pre-scaler control 1 */ -#define S5P_CISCPRERATIO 0x50 -#define S5P_CISCPRERATIO_SHFACTOR(x) ((x) << 28) -#define S5P_CISCPRERATIO_HOR(x) ((x) << 16) -#define S5P_CISCPRERATIO_VER(x) ((x) << 0) - -#define S5P_CISCPREDST 0x54 -#define S5P_CISCPREDST_WIDTH(x) ((x) << 16) -#define S5P_CISCPREDST_HEIGHT(x) ((x) << 0) - -/* Main scaler control */ -#define S5P_CISCCTRL 0x58 -#define S5P_CISCCTRL_SCALERBYPASS (1 << 31) -#define S5P_CISCCTRL_SCALEUP_H (1 << 30) -#define S5P_CISCCTRL_SCALEUP_V (1 << 29) -#define S5P_CISCCTRL_CSCR2Y_WIDE (1 << 28) -#define S5P_CISCCTRL_CSCY2R_WIDE (1 << 27) -#define S5P_CISCCTRL_LCDPATHEN_FIFO (1 << 26) -#define S5P_CISCCTRL_INTERLACE (1 << 25) -#define S5P_CISCCTRL_SCALERSTART (1 << 15) -#define S5P_CISCCTRL_INRGB_FMT_RGB565 (0 << 13) -#define S5P_CISCCTRL_INRGB_FMT_RGB666 (1 << 13) -#define S5P_CISCCTRL_INRGB_FMT_RGB888 (2 << 13) -#define S5P_CISCCTRL_INRGB_FMT_MASK (3 << 13) -#define S5P_CISCCTRL_OUTRGB_FMT_RGB565 (0 << 11) -#define S5P_CISCCTRL_OUTRGB_FMT_RGB666 (1 << 11) -#define S5P_CISCCTRL_OUTRGB_FMT_RGB888 (2 << 11) -#define S5P_CISCCTRL_OUTRGB_FMT_MASK (3 << 11) -#define S5P_CISCCTRL_RGB_EXT (1 << 10) -#define S5P_CISCCTRL_ONE2ONE (1 << 9) -#define S5P_CISCCTRL_MHRATIO(x) ((x) << 16) -#define S5P_CISCCTRL_MVRATIO(x) ((x) << 0) -#define S5P_CISCCTRL_MHRATIO_MASK (0x1ff << 16) -#define S5P_CISCCTRL_MVRATIO_MASK (0x1ff << 0) -#define S5P_CISCCTRL_MHRATIO_EXT(x) (((x) >> 6) << 16) -#define S5P_CISCCTRL_MVRATIO_EXT(x) (((x) >> 6) << 0) - -/* Target area */ -#define S5P_CITAREA 0x5c -#define S5P_CITAREA_MASK 0x0fffffff - -/* General status */ -#define S5P_CISTATUS 0x64 -#define S5P_CISTATUS_OVFIY (1 << 31) -#define S5P_CISTATUS_OVFICB (1 << 30) -#define S5P_CISTATUS_OVFICR (1 << 29) -#define S5P_CISTATUS_VSYNC (1 << 28) -#define S5P_CISTATUS_FRAMECNT_MASK (3 << 26) -#define S5P_CISTATUS_FRAMECNT_SHIFT 26 -#define S5P_CISTATUS_WINOFF_EN (1 << 25) -#define S5P_CISTATUS_IMGCPT_EN (1 << 22) -#define S5P_CISTATUS_IMGCPT_SCEN (1 << 21) -#define S5P_CISTATUS_VSYNC_A (1 << 20) -#define S5P_CISTATUS_VSYNC_B (1 << 19) -#define S5P_CISTATUS_OVRLB (1 << 18) -#define S5P_CISTATUS_FRAME_END (1 << 17) -#define S5P_CISTATUS_LASTCAPT_END (1 << 16) -#define S5P_CISTATUS_VVALID_A (1 << 15) -#define S5P_CISTATUS_VVALID_B (1 << 14) - -/* Indexes to the last and the currently processed buffer. */ -#define S5P_CISTATUS2 0x68 - -/* Image capture control */ -#define S5P_CIIMGCPT 0xc0 -#define S5P_CIIMGCPT_IMGCPTEN (1 << 31) -#define S5P_CIIMGCPT_IMGCPTEN_SC (1 << 30) -#define S5P_CIIMGCPT_CPT_FREN_ENABLE (1 << 25) -#define S5P_CIIMGCPT_CPT_FRMOD_CNT (1 << 18) - -/* Frame capture sequence */ -#define S5P_CICPTSEQ 0xc4 - -/* Image effect */ -#define S5P_CIIMGEFF 0xd0 -#define S5P_CIIMGEFF_IE_ENABLE (1 << 30) -#define S5P_CIIMGEFF_IE_SC_BEFORE (0 << 29) -#define S5P_CIIMGEFF_IE_SC_AFTER (1 << 29) -#define S5P_CIIMGEFF_FIN_BYPASS (0 << 26) -#define S5P_CIIMGEFF_FIN_ARBITRARY (1 << 26) -#define S5P_CIIMGEFF_FIN_NEGATIVE (2 << 26) -#define S5P_CIIMGEFF_FIN_ARTFREEZE (3 << 26) -#define S5P_CIIMGEFF_FIN_EMBOSSING (4 << 26) -#define S5P_CIIMGEFF_FIN_SILHOUETTE (5 << 26) -#define S5P_CIIMGEFF_FIN_MASK (7 << 26) -#define S5P_CIIMGEFF_PAT_CBCR_MASK ((0xff < 13) | (0xff < 0)) -#define S5P_CIIMGEFF_PAT_CB(x) ((x) << 13) -#define S5P_CIIMGEFF_PAT_CR(x) ((x) << 0) - -/* Input DMA Y/Cb/Cr plane start address 0/1 */ -#define S5P_CIIYSA(n) (0xd4 + (n) * 0x70) -#define S5P_CIICBSA(n) (0xd8 + (n) * 0x70) -#define S5P_CIICRSA(n) (0xdc + (n) * 0x70) - -/* Real input DMA image size */ -#define S5P_CIREAL_ISIZE 0xf8 -#define S5P_CIREAL_ISIZE_AUTOLOAD_EN (1 << 31) -#define S5P_CIREAL_ISIZE_ADDR_CH_DIS (1 << 30) -#define S5P_CIREAL_ISIZE_HEIGHT(x) ((x) << 16) -#define S5P_CIREAL_ISIZE_WIDTH(x) ((x) << 0) - - -/* Input DMA control */ -#define S5P_MSCTRL 0xfc -#define S5P_MSCTRL_IN_BURST_COUNT_MASK (0xF << 24) -#define S5P_MSCTRL_2P_IN_ORDER_MASK (3 << 16) -#define S5P_MSCTRL_2P_IN_ORDER_SHIFT 16 -#define S5P_MSCTRL_C_INT_IN_3PLANE (0 << 15) -#define S5P_MSCTRL_C_INT_IN_2PLANE (1 << 15) -#define S5P_MSCTRL_C_INT_IN_MASK (1 << 15) -#define S5P_MSCTRL_FLIP_SHIFT 13 -#define S5P_MSCTRL_FLIP_MASK (3 << 13) -#define S5P_MSCTRL_FLIP_NORMAL (0 << 13) -#define S5P_MSCTRL_FLIP_X_MIRROR (1 << 13) -#define S5P_MSCTRL_FLIP_Y_MIRROR (2 << 13) -#define S5P_MSCTRL_FLIP_180 (3 << 13) -#define S5P_MSCTRL_FIFO_CTRL_FULL (1 << 12) -#define S5P_MSCTRL_ORDER422_SHIFT 4 -#define S5P_MSCTRL_ORDER422_YCBYCR (0 << 4) -#define S5P_MSCTRL_ORDER422_CBYCRY (1 << 4) -#define S5P_MSCTRL_ORDER422_YCRYCB (2 << 4) -#define S5P_MSCTRL_ORDER422_CRYCBY (3 << 4) -#define S5P_MSCTRL_ORDER422_MASK (3 << 4) -#define S5P_MSCTRL_INPUT_EXTCAM (0 << 3) -#define S5P_MSCTRL_INPUT_MEMORY (1 << 3) -#define S5P_MSCTRL_INPUT_MASK (1 << 3) -#define S5P_MSCTRL_INFORMAT_YCBCR420 (0 << 1) -#define S5P_MSCTRL_INFORMAT_YCBCR422 (1 << 1) -#define S5P_MSCTRL_INFORMAT_YCBCR422_1P (2 << 1) -#define S5P_MSCTRL_INFORMAT_RGB (3 << 1) -#define S5P_MSCTRL_INFORMAT_MASK (3 << 1) -#define S5P_MSCTRL_ENVID (1 << 0) -#define S5P_MSCTRL_IN_BURST_COUNT(x) ((x) << 24) - -/* Output DMA Y/Cb/Cr offset */ -#define S5P_CIOYOFF 0x168 -#define S5P_CIOCBOFF 0x16c -#define S5P_CIOCROFF 0x170 - -/* Input DMA Y/Cb/Cr offset */ -#define S5P_CIIYOFF 0x174 -#define S5P_CIICBOFF 0x178 -#define S5P_CIICROFF 0x17c - -#define S5P_CIO_OFFS_VER(x) ((x) << 16) -#define S5P_CIO_OFFS_HOR(x) ((x) << 0) - -/* Input DMA original image size */ -#define S5P_ORGISIZE 0x180 - -/* Output DMA original image size */ -#define S5P_ORGOSIZE 0x184 - -#define S5P_ORIG_SIZE_VER(x) ((x) << 16) -#define S5P_ORIG_SIZE_HOR(x) ((x) << 0) - -/* Real output DMA image size (extension register) */ -#define S5P_CIEXTEN 0x188 -#define S5P_CIEXTEN_MHRATIO_EXT(x) (((x) & 0x3f) << 10) -#define S5P_CIEXTEN_MVRATIO_EXT(x) ((x) & 0x3f) -#define S5P_CIEXTEN_MHRATIO_EXT_MASK (0x3f << 10) -#define S5P_CIEXTEN_MVRATIO_EXT_MASK 0x3f - -#define S5P_CIDMAPARAM 0x18c -#define S5P_CIDMAPARAM_R_LINEAR (0 << 29) -#define S5P_CIDMAPARAM_R_64X32 (3 << 29) -#define S5P_CIDMAPARAM_W_LINEAR (0 << 13) -#define S5P_CIDMAPARAM_W_64X32 (3 << 13) -#define S5P_CIDMAPARAM_TILE_MASK ((3 << 29) | (3 << 13)) - -/* MIPI CSI image format */ -#define S5P_CSIIMGFMT 0x194 -#define S5P_CSIIMGFMT_YCBCR422_8BIT 0x1e -#define S5P_CSIIMGFMT_RAW8 0x2a -#define S5P_CSIIMGFMT_RAW10 0x2b -#define S5P_CSIIMGFMT_RAW12 0x2c -/* User defined formats. x = 0...16. */ -#define S5P_CSIIMGFMT_USER(x) (0x30 + x - 1) - -/* Output frame buffer sequence mask */ -#define S5P_CIFCNTSEQ 0x1FC - -#endif /* REGS_FIMC_H_ */ diff --git a/drivers/media/video/s5p-g2d/g2d.c b/drivers/media/video/s5p-g2d/g2d.c index 789de74014e5..7c98ee7377ee 100644 --- a/drivers/media/video/s5p-g2d/g2d.c +++ b/drivers/media/video/s5p-g2d/g2d.c @@ -65,7 +65,7 @@ static struct g2d_fmt formats[] = { }; #define NUM_FORMATS ARRAY_SIZE(formats) -struct g2d_frame def_frame = { +static struct g2d_frame def_frame = { .width = DEFAULT_WIDTH, .height = DEFAULT_HEIGHT, .c_width = DEFAULT_WIDTH, @@ -77,7 +77,7 @@ struct g2d_frame def_frame = { .bottom = DEFAULT_HEIGHT, }; -struct g2d_fmt *find_fmt(struct v4l2_format *f) +static struct g2d_fmt *find_fmt(struct v4l2_format *f) { unsigned int i; for (i = 0; i < NUM_FORMATS; i++) { @@ -202,7 +202,7 @@ static const struct v4l2_ctrl_ops g2d_ctrl_ops = { .s_ctrl = g2d_s_ctrl, }; -int g2d_setup_ctrls(struct g2d_ctx *ctx) +static int g2d_setup_ctrls(struct g2d_ctx *ctx) { struct g2d_dev *dev = ctx->dev; @@ -546,11 +546,11 @@ static void job_abort(void *prv) struct g2d_dev *dev = ctx->dev; int ret; - if (dev->curr == 0) /* No job currently running */ + if (dev->curr == NULL) /* No job currently running */ return; ret = wait_event_timeout(dev->irq_queue, - dev->curr == 0, + dev->curr == NULL, msecs_to_jiffies(G2D_TIMEOUT)); } @@ -599,19 +599,19 @@ static irqreturn_t g2d_isr(int irq, void *prv) g2d_clear_int(dev); clk_disable(dev->gate); - BUG_ON(ctx == 0); + BUG_ON(ctx == NULL); src = v4l2_m2m_src_buf_remove(ctx->m2m_ctx); dst = v4l2_m2m_dst_buf_remove(ctx->m2m_ctx); - BUG_ON(src == 0); - BUG_ON(dst == 0); + BUG_ON(src == NULL); + BUG_ON(dst == NULL); v4l2_m2m_buf_done(src, VB2_BUF_STATE_DONE); v4l2_m2m_buf_done(dst, VB2_BUF_STATE_DONE); v4l2_m2m_job_finish(dev->m2m_dev, ctx->m2m_ctx); - dev->curr = 0; + dev->curr = NULL; wake_up(&dev->irq_queue); return IRQ_HANDLED; } @@ -674,42 +674,27 @@ static int g2d_probe(struct platform_device *pdev) struct resource *res; int ret = 0; - dev = kzalloc(sizeof(*dev), GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); if (!dev) return -ENOMEM; + spin_lock_init(&dev->ctrl_lock); mutex_init(&dev->mutex); atomic_set(&dev->num_inst, 0); init_waitqueue_head(&dev->irq_queue); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "failed to find registers\n"); - ret = -ENOENT; - goto free_dev; - } - dev->res_regs = request_mem_region(res->start, resource_size(res), - dev_name(&pdev->dev)); - - if (!dev->res_regs) { - dev_err(&pdev->dev, "failed to obtain register region\n"); - ret = -ENOENT; - goto free_dev; - } - - dev->regs = ioremap(res->start, resource_size(res)); - if (!dev->regs) { - dev_err(&pdev->dev, "failed to map registers\n"); - ret = -ENOENT; - goto rel_res_regs; + dev->regs = devm_request_and_ioremap(&pdev->dev, res); + if (dev->regs == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; } dev->clk = clk_get(&pdev->dev, "sclk_fimg2d"); if (IS_ERR_OR_NULL(dev->clk)) { dev_err(&pdev->dev, "failed to get g2d clock\n"); - ret = -ENXIO; - goto unmap_regs; + return -ENXIO; } ret = clk_prepare(dev->clk); @@ -740,7 +725,8 @@ static int g2d_probe(struct platform_device *pdev) dev->irq = res->start; - ret = request_irq(dev->irq, g2d_isr, 0, pdev->name, dev); + ret = devm_request_irq(&pdev->dev, dev->irq, g2d_isr, + 0, pdev->name, dev); if (ret) { dev_err(&pdev->dev, "failed to install IRQ\n"); goto put_clk_gate; @@ -749,7 +735,7 @@ static int g2d_probe(struct platform_device *pdev) dev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(dev->alloc_ctx)) { ret = PTR_ERR(dev->alloc_ctx); - goto rel_irq; + goto unprep_clk_gate; } ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); @@ -762,6 +748,10 @@ static int g2d_probe(struct platform_device *pdev) goto unreg_v4l2_dev; } *vfd = g2d_videodev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->lock = &dev->mutex; ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0); if (ret) { @@ -793,8 +783,6 @@ unreg_v4l2_dev: v4l2_device_unregister(&dev->v4l2_dev); alloc_ctx_cleanup: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); -rel_irq: - free_irq(dev->irq, dev); unprep_clk_gate: clk_unprepare(dev->gate); put_clk_gate: @@ -803,12 +791,7 @@ unprep_clk: clk_unprepare(dev->clk); put_clk: clk_put(dev->clk); -unmap_regs: - iounmap(dev->regs); -rel_res_regs: - release_resource(dev->res_regs); -free_dev: - kfree(dev); + return ret; } @@ -821,14 +804,10 @@ static int g2d_remove(struct platform_device *pdev) video_unregister_device(dev->vfd); v4l2_device_unregister(&dev->v4l2_dev); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx); - free_irq(dev->irq, dev); clk_unprepare(dev->gate); clk_put(dev->gate); clk_unprepare(dev->clk); clk_put(dev->clk); - iounmap(dev->regs); - release_resource(dev->res_regs); - kfree(dev); return 0; } diff --git a/drivers/media/video/s5p-g2d/g2d.h b/drivers/media/video/s5p-g2d/g2d.h index 1b82065aeaef..6b765b0216c5 100644 --- a/drivers/media/video/s5p-g2d/g2d.h +++ b/drivers/media/video/s5p-g2d/g2d.h @@ -23,7 +23,6 @@ struct g2d_dev { spinlock_t ctrl_lock; atomic_t num_inst; struct vb2_alloc_ctx *alloc_ctx; - struct resource *res_regs; void __iomem *regs; struct clk *clk; struct clk *gate; diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.c b/drivers/media/video/s5p-jpeg/jpeg-core.c index 5a49c307f9c1..28b5225d94f5 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.c +++ b/drivers/media/video/s5p-jpeg/jpeg-core.c @@ -813,7 +813,7 @@ static int s5p_jpeg_streamoff(struct file *file, void *priv, return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type); } -int s5p_jpeg_g_selection(struct file *file, void *priv, +static int s5p_jpeg_g_selection(struct file *file, void *priv, struct v4l2_selection *s) { struct s5p_jpeg_ctx *ctx = fh_to_ctx(priv); @@ -1290,7 +1290,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) int ret; /* JPEG IP abstraction struct */ - jpeg = kzalloc(sizeof(struct s5p_jpeg), GFP_KERNEL); + jpeg = devm_kzalloc(&pdev->dev, sizeof(struct s5p_jpeg), GFP_KERNEL); if (!jpeg) return -ENOMEM; @@ -1300,43 +1300,25 @@ static int s5p_jpeg_probe(struct platform_device *pdev) /* memory-mapped registers */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "cannot find IO resource\n"); - ret = -ENOENT; - goto jpeg_alloc_rollback; - } - jpeg->ioarea = request_mem_region(res->start, resource_size(res), - pdev->name); - if (!jpeg->ioarea) { - dev_err(&pdev->dev, "cannot request IO\n"); - ret = -ENXIO; - goto jpeg_alloc_rollback; + jpeg->regs = devm_request_and_ioremap(&pdev->dev, res); + if (jpeg->regs == NULL) { + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; } - jpeg->regs = ioremap(res->start, resource_size(res)); - if (!jpeg->regs) { - dev_err(&pdev->dev, "cannot map IO\n"); - ret = -ENXIO; - goto mem_region_rollback; - } - - dev_dbg(&pdev->dev, "registers %p (%p, %p)\n", - jpeg->regs, jpeg->ioarea, res); - /* interrupt service routine registration */ jpeg->irq = ret = platform_get_irq(pdev, 0); if (ret < 0) { dev_err(&pdev->dev, "cannot find IRQ\n"); - goto ioremap_rollback; + return ret; } - ret = request_irq(jpeg->irq, s5p_jpeg_irq, 0, - dev_name(&pdev->dev), jpeg); - + ret = devm_request_irq(&pdev->dev, jpeg->irq, s5p_jpeg_irq, 0, + dev_name(&pdev->dev), jpeg); if (ret) { dev_err(&pdev->dev, "cannot claim IRQ %d\n", jpeg->irq); - goto ioremap_rollback; + return ret; } /* clocks */ @@ -1344,7 +1326,7 @@ static int s5p_jpeg_probe(struct platform_device *pdev) if (IS_ERR(jpeg->clk)) { dev_err(&pdev->dev, "cannot get clock\n"); ret = PTR_ERR(jpeg->clk); - goto request_irq_rollback; + return ret; } dev_dbg(&pdev->dev, "clock source %p\n", jpeg->clk); clk_enable(jpeg->clk); @@ -1386,6 +1368,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_encoder->release = video_device_release; jpeg->vfd_encoder->lock = &jpeg->lock; jpeg->vfd_encoder->v4l2_dev = &jpeg->v4l2_dev; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_encoder->flags); ret = video_register_device(jpeg->vfd_encoder, VFL_TYPE_GRABBER, -1); if (ret) { @@ -1413,6 +1399,10 @@ static int s5p_jpeg_probe(struct platform_device *pdev) jpeg->vfd_decoder->release = video_device_release; jpeg->vfd_decoder->lock = &jpeg->lock; jpeg->vfd_decoder->v4l2_dev = &jpeg->v4l2_dev; + /* Locking in file operations other than ioctl should be done by the driver, + not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &jpeg->vfd_decoder->flags); ret = video_register_device(jpeg->vfd_decoder, VFL_TYPE_GRABBER, -1); if (ret) { @@ -1456,18 +1446,6 @@ clk_get_rollback: clk_disable(jpeg->clk); clk_put(jpeg->clk); -request_irq_rollback: - free_irq(jpeg->irq, jpeg); - -ioremap_rollback: - iounmap(jpeg->regs); - -mem_region_rollback: - release_resource(jpeg->ioarea); - release_mem_region(jpeg->ioarea->start, resource_size(jpeg->ioarea)); - -jpeg_alloc_rollback: - kfree(jpeg); return ret; } @@ -1488,14 +1466,6 @@ static int s5p_jpeg_remove(struct platform_device *pdev) clk_disable(jpeg->clk); clk_put(jpeg->clk); - free_irq(jpeg->irq, jpeg); - - iounmap(jpeg->regs); - - release_resource(jpeg->ioarea); - release_mem_region(jpeg->ioarea->start, resource_size(jpeg->ioarea)); - kfree(jpeg); - return 0; } diff --git a/drivers/media/video/s5p-jpeg/jpeg-core.h b/drivers/media/video/s5p-jpeg/jpeg-core.h index 38d7367f7a6d..9d0cd2b76f61 100644 --- a/drivers/media/video/s5p-jpeg/jpeg-core.h +++ b/drivers/media/video/s5p-jpeg/jpeg-core.h @@ -54,7 +54,6 @@ * @vfd_encoder: video device node for encoder mem2mem mode * @vfd_decoder: video device node for decoder mem2mem mode * @m2m_dev: v4l2 mem2mem device data - * @ioarea: JPEG IP memory region * @regs: JPEG IP registers mapping * @irq: JPEG IP irq * @clk: JPEG IP clock @@ -70,7 +69,6 @@ struct s5p_jpeg { struct video_device *vfd_decoder; struct v4l2_m2m_dev *m2m_dev; - struct resource *ioarea; void __iomem *regs; unsigned int irq; struct clk *clk; diff --git a/drivers/media/video/s5p-mfc/s5p_mfc.c b/drivers/media/video/s5p-mfc/s5p_mfc.c index 83fe461af263..9bb68e7b5ae8 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc.c @@ -70,7 +70,7 @@ static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason, wake_up(&dev->queue); } -void s5p_mfc_watchdog(unsigned long arg) +static void s5p_mfc_watchdog(unsigned long arg) { struct s5p_mfc_dev *dev = (struct s5p_mfc_dev *)arg; @@ -373,7 +373,7 @@ static void s5p_mfc_handle_error(struct s5p_mfc_ctx *ctx, /* If no context is available then all necessary * processing has been done. */ - if (ctx == 0) + if (ctx == NULL) return; dev = ctx->dev; @@ -429,7 +429,7 @@ static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, struct s5p_mfc_dev *dev; unsigned int guard_width, guard_height; - if (ctx == 0) + if (ctx == NULL) return; dev = ctx->dev; if (ctx->c_ops->post_seq_start) { @@ -496,7 +496,7 @@ static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, struct s5p_mfc_dev *dev; unsigned long flags; - if (ctx == 0) + if (ctx == NULL) return; dev = ctx->dev; s5p_mfc_clear_int_flags(dev); @@ -772,7 +772,7 @@ err_queue_init: err_init_hw: s5p_mfc_release_firmware(dev); err_alloc_fw: - dev->ctx[ctx->num] = 0; + dev->ctx[ctx->num] = NULL; del_timer_sync(&dev->watchdog_timer); s5p_mfc_clock_off(); err_pwr_enable: @@ -849,7 +849,7 @@ static int s5p_mfc_release(struct file *file) } mfc_debug(2, "Shutting down clock\n"); s5p_mfc_clock_off(); - dev->ctx[ctx->num] = 0; + dev->ctx[ctx->num] = NULL; s5p_mfc_dec_ctrls_delete(ctx); v4l2_fh_del(&ctx->fh); v4l2_fh_exit(&ctx->fh); @@ -948,7 +948,7 @@ static int s5p_mfc_probe(struct platform_device *pdev) int ret; pr_debug("%s++\n", __func__); - dev = kzalloc(sizeof *dev, GFP_KERNEL); + dev = devm_kzalloc(&pdev->dev, sizeof *dev, GFP_KERNEL); if (!dev) { dev_err(&pdev->dev, "Not enough memory for MFC device\n"); return -ENOMEM; @@ -959,49 +959,35 @@ static int s5p_mfc_probe(struct platform_device *pdev) dev->plat_dev = pdev; if (!dev->plat_dev) { dev_err(&pdev->dev, "No platform data specified\n"); - ret = -ENODEV; - goto err_dev; + return -ENODEV; } ret = s5p_mfc_init_pm(dev); if (ret < 0) { dev_err(&pdev->dev, "failed to get mfc clock source\n"); - goto err_clk; + return ret; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "failed to get memory region resource\n"); - ret = -ENOENT; - goto err_res; - } - dev->mfc_mem = request_mem_region(res->start, resource_size(res), - pdev->name); - if (dev->mfc_mem == NULL) { - dev_err(&pdev->dev, "failed to get memory region\n"); - ret = -ENOENT; - goto err_mem_reg; - } - dev->regs_base = ioremap(dev->mfc_mem->start, resource_size(dev->mfc_mem)); + dev->regs_base = devm_request_and_ioremap(&pdev->dev, res); if (dev->regs_base == NULL) { - dev_err(&pdev->dev, "failed to ioremap address region\n"); - ret = -ENOENT; - goto err_ioremap; + dev_err(&pdev->dev, "Failed to obtain io memory\n"); + return -ENOENT; } res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); if (res == NULL) { dev_err(&pdev->dev, "failed to get irq resource\n"); ret = -ENOENT; - goto err_get_res; + goto err_res; } dev->irq = res->start; - ret = request_irq(dev->irq, s5p_mfc_irq, IRQF_DISABLED, pdev->name, - dev); + ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq, + IRQF_DISABLED, pdev->name, dev); if (ret) { dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); - goto err_req_irq; + goto err_res; } dev->mem_dev_l = device_find_child(&dev->plat_dev->dev, "s5p-mfc-l", @@ -1009,20 +995,20 @@ static int s5p_mfc_probe(struct platform_device *pdev) if (!dev->mem_dev_l) { mfc_err("Mem child (L) device get failed\n"); ret = -ENODEV; - goto err_find_child; + goto err_res; } dev->mem_dev_r = device_find_child(&dev->plat_dev->dev, "s5p-mfc-r", match_child); if (!dev->mem_dev_r) { mfc_err("Mem child (R) device get failed\n"); ret = -ENODEV; - goto err_find_child; + goto err_res; } dev->alloc_ctx[0] = vb2_dma_contig_init_ctx(dev->mem_dev_l); if (IS_ERR_OR_NULL(dev->alloc_ctx[0])) { ret = PTR_ERR(dev->alloc_ctx[0]); - goto err_mem_init_ctx_0; + goto err_res; } dev->alloc_ctx[1] = vb2_dma_contig_init_ctx(dev->mem_dev_r); if (IS_ERR_OR_NULL(dev->alloc_ctx[1])) { @@ -1048,6 +1034,10 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->ioctl_ops = get_dec_v4l2_ioctl_ops(); vfd->release = video_device_release, vfd->lock = &dev->mfc_mutex; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->v4l2_dev = &dev->v4l2_dev; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); dev->vfd_dec = vfd; @@ -1072,6 +1062,8 @@ static int s5p_mfc_probe(struct platform_device *pdev) vfd->ioctl_ops = get_enc_v4l2_ioctl_ops(); vfd->release = video_device_release, vfd->lock = &dev->mfc_mutex; + /* This should not be necessary */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); vfd->v4l2_dev = &dev->v4l2_dev; snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); dev->vfd_enc = vfd; @@ -1110,22 +1102,9 @@ err_v4l2_dev_reg: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); err_mem_init_ctx_1: vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); -err_mem_init_ctx_0: -err_find_child: - free_irq(dev->irq, dev); -err_req_irq: -err_get_res: - iounmap(dev->regs_base); - dev->regs_base = NULL; -err_ioremap: - release_resource(dev->mfc_mem); - kfree(dev->mfc_mem); -err_mem_reg: err_res: s5p_mfc_final_pm(dev); -err_clk: -err_dev: - kfree(dev); + pr_debug("%s-- with error\n", __func__); return ret; @@ -1148,15 +1127,7 @@ static int __devexit s5p_mfc_remove(struct platform_device *pdev) vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[0]); vb2_dma_contig_cleanup_ctx(dev->alloc_ctx[1]); - free_irq(dev->irq, dev); - iounmap(dev->regs_base); - if (dev->mfc_mem) { - release_resource(dev->mfc_mem); - kfree(dev->mfc_mem); - dev->mfc_mem = NULL; - } s5p_mfc_final_pm(dev); - kfree(dev); return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_common.h b/drivers/media/video/s5p-mfc/s5p_mfc_common.h index 91146fa622e4..bd5706a6bad1 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_common.h +++ b/drivers/media/video/s5p-mfc/s5p_mfc_common.h @@ -185,7 +185,6 @@ struct s5p_mfc_pm { * @mem_dev_r: child device of the right memory bank (1) * @regs_base: base address of the MFC hw registers * @irq: irq resource - * @mfc_mem: MFC registers memory resource * @dec_ctrl_handler: control framework handler for decoding * @enc_ctrl_handler: control framework handler for encoding * @pm: power management control @@ -221,7 +220,6 @@ struct s5p_mfc_dev { struct device *mem_dev_r; void __iomem *regs_base; int irq; - struct resource *mfc_mem; struct v4l2_ctrl_handler dec_ctrl_handler; struct v4l2_ctrl_handler enc_ctrl_handler; struct s5p_mfc_pm pm; diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c index f2481a85e0a2..08a5cfeaa59e 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_ctrl.c @@ -52,7 +52,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) s5p_mfc_bitproc_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], dev->fw_size); if (IS_ERR(s5p_mfc_bitproc_buf)) { - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; mfc_err("Allocating bitprocessor buffer failed\n"); release_firmware(fw_blob); return -ENOMEM; @@ -63,7 +63,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("The base memory for bank 1 is not aligned to 128KB\n"); vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; release_firmware(fw_blob); return -EIO; } @@ -72,7 +72,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("Bitprocessor memory remap failed\n"); vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; release_firmware(fw_blob); return -EIO; } @@ -82,7 +82,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) if (IS_ERR(b_base)) { vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; mfc_err("Allocating bank2 base failed\n"); release_firmware(fw_blob); return -ENOMEM; @@ -94,7 +94,7 @@ int s5p_mfc_alloc_and_load_firmware(struct s5p_mfc_dev *dev) mfc_err("The base memory for bank 2 is not aligned to 128KB\n"); vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; release_firmware(fw_blob); return -EIO; } @@ -126,7 +126,7 @@ int s5p_mfc_reload_firmware(struct s5p_mfc_dev *dev) release_firmware(fw_blob); return -ENOMEM; } - if (s5p_mfc_bitproc_buf == 0 || s5p_mfc_bitproc_phys == 0) { + if (s5p_mfc_bitproc_buf == NULL || s5p_mfc_bitproc_phys == 0) { mfc_err("MFC firmware is not allocated or was not mapped correctly\n"); release_firmware(fw_blob); return -EINVAL; @@ -146,9 +146,9 @@ int s5p_mfc_release_firmware(struct s5p_mfc_dev *dev) if (!s5p_mfc_bitproc_buf) return -EINVAL; vb2_dma_contig_memops.put(s5p_mfc_bitproc_buf); - s5p_mfc_bitproc_virt = 0; + s5p_mfc_bitproc_virt = NULL; s5p_mfc_bitproc_phys = 0; - s5p_mfc_bitproc_buf = 0; + s5p_mfc_bitproc_buf = NULL; return 0; } diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c index dff9dc798795..acedb2004be3 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_enc.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_enc.c @@ -1436,7 +1436,8 @@ static const struct v4l2_ctrl_ops s5p_mfc_enc_ctrl_ops = { .s_ctrl = s5p_mfc_enc_s_ctrl, }; -int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a) +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) { struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); @@ -1452,7 +1453,8 @@ int vidioc_s_parm(struct file *file, void *priv, struct v4l2_streamparm *a) return 0; } -int vidioc_g_parm(struct file *file, void *priv, struct v4l2_streamparm *a) +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *a) { struct s5p_mfc_ctx *ctx = fh_to_ctx(priv); diff --git a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c index e08b21c50ebf..e6217cbfa4a3 100644 --- a/drivers/media/video/s5p-mfc/s5p_mfc_opr.c +++ b/drivers/media/video/s5p-mfc/s5p_mfc_opr.c @@ -43,7 +43,7 @@ int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx) ctx->desc_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], DESC_BUF_SIZE); if (IS_ERR_VALUE((int)ctx->desc_buf)) { - ctx->desc_buf = 0; + ctx->desc_buf = NULL; mfc_err("Allocating DESC buffer failed\n"); return -ENOMEM; } @@ -54,7 +54,7 @@ int s5p_mfc_alloc_dec_temp_buffers(struct s5p_mfc_ctx *ctx) if (desc_virt == NULL) { vb2_dma_contig_memops.put(ctx->desc_buf); ctx->desc_phys = 0; - ctx->desc_buf = 0; + ctx->desc_buf = NULL; mfc_err("Remapping DESC buffer failed\n"); return -ENOMEM; } @@ -69,7 +69,7 @@ void s5p_mfc_release_dec_desc_buffer(struct s5p_mfc_ctx *ctx) if (ctx->desc_phys) { vb2_dma_contig_memops.put(ctx->desc_buf); ctx->desc_phys = 0; - ctx->desc_buf = 0; + ctx->desc_buf = NULL; } } @@ -186,7 +186,7 @@ int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx) ctx->bank1_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK1_ALLOC_CTX], ctx->bank1_size); if (IS_ERR(ctx->bank1_buf)) { - ctx->bank1_buf = 0; + ctx->bank1_buf = NULL; printk(KERN_ERR "Buf alloc for decoding failed (port A)\n"); return -ENOMEM; @@ -200,7 +200,7 @@ int s5p_mfc_alloc_codec_buffers(struct s5p_mfc_ctx *ctx) ctx->bank2_buf = vb2_dma_contig_memops.alloc( dev->alloc_ctx[MFC_BANK2_ALLOC_CTX], ctx->bank2_size); if (IS_ERR(ctx->bank2_buf)) { - ctx->bank2_buf = 0; + ctx->bank2_buf = NULL; mfc_err("Buf alloc for decoding failed (port B)\n"); return -ENOMEM; } @@ -216,13 +216,13 @@ void s5p_mfc_release_codec_buffers(struct s5p_mfc_ctx *ctx) { if (ctx->bank1_buf) { vb2_dma_contig_memops.put(ctx->bank1_buf); - ctx->bank1_buf = 0; + ctx->bank1_buf = NULL; ctx->bank1_phys = 0; ctx->bank1_size = 0; } if (ctx->bank2_buf) { vb2_dma_contig_memops.put(ctx->bank2_buf); - ctx->bank2_buf = 0; + ctx->bank2_buf = NULL; ctx->bank2_phys = 0; ctx->bank2_size = 0; } @@ -244,7 +244,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx) if (IS_ERR(ctx->ctx_buf)) { mfc_err("Allocating context buffer failed\n"); ctx->ctx_phys = 0; - ctx->ctx_buf = 0; + ctx->ctx_buf = NULL; return -ENOMEM; } ctx->ctx_phys = s5p_mfc_mem_cookie( @@ -256,7 +256,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx) mfc_err("Remapping instance buffer failed\n"); vb2_dma_contig_memops.put(ctx->ctx_buf); ctx->ctx_phys = 0; - ctx->ctx_buf = 0; + ctx->ctx_buf = NULL; return -ENOMEM; } /* Zero content of the allocated memory */ @@ -265,7 +265,7 @@ int s5p_mfc_alloc_instance_buffer(struct s5p_mfc_ctx *ctx) if (s5p_mfc_init_shm(ctx) < 0) { vb2_dma_contig_memops.put(ctx->ctx_buf); ctx->ctx_phys = 0; - ctx->ctx_buf = 0; + ctx->ctx_buf = NULL; return -ENOMEM; } return 0; @@ -277,12 +277,12 @@ void s5p_mfc_release_instance_buffer(struct s5p_mfc_ctx *ctx) if (ctx->ctx_buf) { vb2_dma_contig_memops.put(ctx->ctx_buf); ctx->ctx_phys = 0; - ctx->ctx_buf = 0; + ctx->ctx_buf = NULL; } if (ctx->shm_alloc) { vb2_dma_contig_memops.put(ctx->shm_alloc); - ctx->shm_alloc = 0; - ctx->shm = 0; + ctx->shm_alloc = NULL; + ctx->shm = NULL; } } @@ -296,7 +296,7 @@ void s5p_mfc_set_dec_desc_buffer(struct s5p_mfc_ctx *ctx) } /* Set registers for shared buffer */ -void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx) +static void s5p_mfc_set_shared_buffer(struct s5p_mfc_ctx *ctx) { struct s5p_mfc_dev *dev = ctx->dev; mfc_write(dev, ctx->shm_ofs, S5P_FIMV_SI_CH0_HOST_WR_ADR); diff --git a/drivers/media/video/s5p-tv/hdmi_drv.c b/drivers/media/video/s5p-tv/hdmi_drv.c index 4865d25a0e57..20cb6eef2979 100644 --- a/drivers/media/video/s5p-tv/hdmi_drv.c +++ b/drivers/media/video/s5p-tv/hdmi_drv.c @@ -42,7 +42,23 @@ MODULE_DESCRIPTION("Samsung HDMI"); MODULE_LICENSE("GPL"); /* default preset configured on probe */ -#define HDMI_DEFAULT_PRESET V4L2_DV_1080P60 +#define HDMI_DEFAULT_PRESET V4L2_DV_480P59_94 + +struct hdmi_pulse { + u32 beg; + u32 end; +}; + +struct hdmi_timings { + struct hdmi_pulse hact; + u32 hsyn_pol; /* 0 - high, 1 - low */ + struct hdmi_pulse hsyn; + u32 interlaced; + struct hdmi_pulse vact[2]; + u32 vsyn_pol; /* 0 - high, 1 - low */ + u32 vsyn_off; + struct hdmi_pulse vsyn[2]; +}; struct hdmi_resources { struct clk *hdmi; @@ -70,64 +86,15 @@ struct hdmi_device { /** subdev of MHL interface */ struct v4l2_subdev *mhl_sd; /** configuration of current graphic mode */ - const struct hdmi_preset_conf *cur_conf; + const struct hdmi_timings *cur_conf; + /** flag indicating that timings are dirty */ + int cur_conf_dirty; /** current preset */ u32 cur_preset; /** other resources */ struct hdmi_resources res; }; -struct hdmi_tg_regs { - u8 cmd; - u8 h_fsz_l; - u8 h_fsz_h; - u8 hact_st_l; - u8 hact_st_h; - u8 hact_sz_l; - u8 hact_sz_h; - u8 v_fsz_l; - u8 v_fsz_h; - u8 vsync_l; - u8 vsync_h; - u8 vsync2_l; - u8 vsync2_h; - u8 vact_st_l; - u8 vact_st_h; - u8 vact_sz_l; - u8 vact_sz_h; - u8 field_chg_l; - u8 field_chg_h; - u8 vact_st2_l; - u8 vact_st2_h; - u8 vsync_top_hdmi_l; - u8 vsync_top_hdmi_h; - u8 vsync_bot_hdmi_l; - u8 vsync_bot_hdmi_h; - u8 field_top_hdmi_l; - u8 field_top_hdmi_h; - u8 field_bot_hdmi_l; - u8 field_bot_hdmi_h; -}; - -struct hdmi_core_regs { - u8 h_blank[2]; - u8 v_blank[3]; - u8 h_v_line[3]; - u8 vsync_pol[1]; - u8 int_pro_mode[1]; - u8 v_blank_f[3]; - u8 h_sync_gen[3]; - u8 v_sync_gen1[3]; - u8 v_sync_gen2[3]; - u8 v_sync_gen3[3]; -}; - -struct hdmi_preset_conf { - struct hdmi_core_regs core; - struct hdmi_tg_regs tg; - struct v4l2_mbus_framefmt mbus_fmt; -}; - static struct platform_device_id hdmi_driver_types[] = { { .name = "s5pv210-hdmi", @@ -165,6 +132,21 @@ void hdmi_writeb(struct hdmi_device *hdev, u32 reg_id, u8 value) writeb(value, hdev->regs + reg_id); } +static inline +void hdmi_writebn(struct hdmi_device *hdev, u32 reg_id, int n, u32 value) +{ + switch (n) { + default: + writeb(value >> 24, hdev->regs + reg_id + 12); + case 3: + writeb(value >> 16, hdev->regs + reg_id + 8); + case 2: + writeb(value >> 8, hdev->regs + reg_id + 4); + case 1: + writeb(value >> 0, hdev->regs + reg_id + 0); + } +} + static inline u32 hdmi_read(struct hdmi_device *hdev, u32 reg_id) { return readl(hdev->regs + reg_id); @@ -211,77 +193,72 @@ static void hdmi_reg_init(struct hdmi_device *hdev) } static void hdmi_timing_apply(struct hdmi_device *hdev, - const struct hdmi_preset_conf *conf) + const struct hdmi_timings *t) { - const struct hdmi_core_regs *core = &conf->core; - const struct hdmi_tg_regs *tg = &conf->tg; - /* setting core registers */ - hdmi_writeb(hdev, HDMI_H_BLANK_0, core->h_blank[0]); - hdmi_writeb(hdev, HDMI_H_BLANK_1, core->h_blank[1]); - hdmi_writeb(hdev, HDMI_V_BLANK_0, core->v_blank[0]); - hdmi_writeb(hdev, HDMI_V_BLANK_1, core->v_blank[1]); - hdmi_writeb(hdev, HDMI_V_BLANK_2, core->v_blank[2]); - hdmi_writeb(hdev, HDMI_H_V_LINE_0, core->h_v_line[0]); - hdmi_writeb(hdev, HDMI_H_V_LINE_1, core->h_v_line[1]); - hdmi_writeb(hdev, HDMI_H_V_LINE_2, core->h_v_line[2]); - hdmi_writeb(hdev, HDMI_VSYNC_POL, core->vsync_pol[0]); - hdmi_writeb(hdev, HDMI_INT_PRO_MODE, core->int_pro_mode[0]); - hdmi_writeb(hdev, HDMI_V_BLANK_F_0, core->v_blank_f[0]); - hdmi_writeb(hdev, HDMI_V_BLANK_F_1, core->v_blank_f[1]); - hdmi_writeb(hdev, HDMI_V_BLANK_F_2, core->v_blank_f[2]); - hdmi_writeb(hdev, HDMI_H_SYNC_GEN_0, core->h_sync_gen[0]); - hdmi_writeb(hdev, HDMI_H_SYNC_GEN_1, core->h_sync_gen[1]); - hdmi_writeb(hdev, HDMI_H_SYNC_GEN_2, core->h_sync_gen[2]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_0, core->v_sync_gen1[0]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_1, core->v_sync_gen1[1]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_1_2, core->v_sync_gen1[2]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_0, core->v_sync_gen2[0]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_1, core->v_sync_gen2[1]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_2_2, core->v_sync_gen2[2]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_0, core->v_sync_gen3[0]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_1, core->v_sync_gen3[1]); - hdmi_writeb(hdev, HDMI_V_SYNC_GEN_3_2, core->v_sync_gen3[2]); + hdmi_writebn(hdev, HDMI_H_BLANK_0, 2, t->hact.beg); + hdmi_writebn(hdev, HDMI_H_SYNC_GEN_0, 3, + (t->hsyn_pol << 20) | (t->hsyn.end << 10) | t->hsyn.beg); + hdmi_writeb(hdev, HDMI_VSYNC_POL, t->vsyn_pol); + hdmi_writebn(hdev, HDMI_V_BLANK_0, 3, + (t->vact[0].beg << 11) | t->vact[0].end); + hdmi_writebn(hdev, HDMI_V_SYNC_GEN_1_0, 3, + (t->vsyn[0].beg << 12) | t->vsyn[0].end); + if (t->interlaced) { + u32 vsyn_trans = t->hsyn.beg + t->vsyn_off; + + hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 1); + hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3, + (t->hact.end << 12) | t->vact[1].end); + hdmi_writebn(hdev, HDMI_V_BLANK_F_0, 3, + (t->vact[1].end << 11) | t->vact[1].beg); + hdmi_writebn(hdev, HDMI_V_SYNC_GEN_2_0, 3, + (t->vsyn[1].beg << 12) | t->vsyn[1].end); + hdmi_writebn(hdev, HDMI_V_SYNC_GEN_3_0, 3, + (vsyn_trans << 12) | vsyn_trans); + } else { + hdmi_writeb(hdev, HDMI_INT_PRO_MODE, 0); + hdmi_writebn(hdev, HDMI_H_V_LINE_0, 3, + (t->hact.end << 12) | t->vact[0].end); + } + /* Timing generator registers */ - hdmi_writeb(hdev, HDMI_TG_H_FSZ_L, tg->h_fsz_l); - hdmi_writeb(hdev, HDMI_TG_H_FSZ_H, tg->h_fsz_h); - hdmi_writeb(hdev, HDMI_TG_HACT_ST_L, tg->hact_st_l); - hdmi_writeb(hdev, HDMI_TG_HACT_ST_H, tg->hact_st_h); - hdmi_writeb(hdev, HDMI_TG_HACT_SZ_L, tg->hact_sz_l); - hdmi_writeb(hdev, HDMI_TG_HACT_SZ_H, tg->hact_sz_h); - hdmi_writeb(hdev, HDMI_TG_V_FSZ_L, tg->v_fsz_l); - hdmi_writeb(hdev, HDMI_TG_V_FSZ_H, tg->v_fsz_h); - hdmi_writeb(hdev, HDMI_TG_VSYNC_L, tg->vsync_l); - hdmi_writeb(hdev, HDMI_TG_VSYNC_H, tg->vsync_h); - hdmi_writeb(hdev, HDMI_TG_VSYNC2_L, tg->vsync2_l); - hdmi_writeb(hdev, HDMI_TG_VSYNC2_H, tg->vsync2_h); - hdmi_writeb(hdev, HDMI_TG_VACT_ST_L, tg->vact_st_l); - hdmi_writeb(hdev, HDMI_TG_VACT_ST_H, tg->vact_st_h); - hdmi_writeb(hdev, HDMI_TG_VACT_SZ_L, tg->vact_sz_l); - hdmi_writeb(hdev, HDMI_TG_VACT_SZ_H, tg->vact_sz_h); - hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_L, tg->field_chg_l); - hdmi_writeb(hdev, HDMI_TG_FIELD_CHG_H, tg->field_chg_h); - hdmi_writeb(hdev, HDMI_TG_VACT_ST2_L, tg->vact_st2_l); - hdmi_writeb(hdev, HDMI_TG_VACT_ST2_H, tg->vact_st2_h); - hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, tg->vsync_top_hdmi_l); - hdmi_writeb(hdev, HDMI_TG_VSYNC_TOP_HDMI_H, tg->vsync_top_hdmi_h); - hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, tg->vsync_bot_hdmi_l); - hdmi_writeb(hdev, HDMI_TG_VSYNC_BOT_HDMI_H, tg->vsync_bot_hdmi_h); - hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_L, tg->field_top_hdmi_l); - hdmi_writeb(hdev, HDMI_TG_FIELD_TOP_HDMI_H, tg->field_top_hdmi_h); - hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_L, tg->field_bot_hdmi_l); - hdmi_writeb(hdev, HDMI_TG_FIELD_BOT_HDMI_H, tg->field_bot_hdmi_h); + hdmi_writebn(hdev, HDMI_TG_H_FSZ_L, 2, t->hact.end); + hdmi_writebn(hdev, HDMI_TG_HACT_ST_L, 2, t->hact.beg); + hdmi_writebn(hdev, HDMI_TG_HACT_SZ_L, 2, t->hact.end - t->hact.beg); + hdmi_writebn(hdev, HDMI_TG_VSYNC_L, 2, t->vsyn[0].beg); + hdmi_writebn(hdev, HDMI_TG_VACT_ST_L, 2, t->vact[0].beg); + hdmi_writebn(hdev, HDMI_TG_VACT_SZ_L, 2, + t->vact[0].end - t->vact[0].beg); + hdmi_writebn(hdev, HDMI_TG_VSYNC_TOP_HDMI_L, 2, t->vsyn[0].beg); + hdmi_writebn(hdev, HDMI_TG_FIELD_TOP_HDMI_L, 2, t->vsyn[0].beg); + if (t->interlaced) { + hdmi_write_mask(hdev, HDMI_TG_CMD, ~0, HDMI_TG_FIELD_EN); + hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[1].end); + hdmi_writebn(hdev, HDMI_TG_VSYNC2_L, 2, t->vsyn[1].beg); + hdmi_writebn(hdev, HDMI_TG_FIELD_CHG_L, 2, t->vact[0].end); + hdmi_writebn(hdev, HDMI_TG_VACT_ST2_L, 2, t->vact[1].beg); + hdmi_writebn(hdev, HDMI_TG_VSYNC_BOT_HDMI_L, 2, t->vsyn[1].beg); + hdmi_writebn(hdev, HDMI_TG_FIELD_BOT_HDMI_L, 2, t->vsyn[1].beg); + } else { + hdmi_write_mask(hdev, HDMI_TG_CMD, 0, HDMI_TG_FIELD_EN); + hdmi_writebn(hdev, HDMI_TG_V_FSZ_L, 2, t->vact[0].end); + } } static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) { struct device *dev = hdmi_dev->dev; - const struct hdmi_preset_conf *conf = hdmi_dev->cur_conf; + const struct hdmi_timings *conf = hdmi_dev->cur_conf; struct v4l2_dv_preset preset; int ret; dev_dbg(dev, "%s\n", __func__); + /* skip if conf is already synchronized with HW */ + if (!hdmi_dev->cur_conf_dirty) + return 0; + /* reset hdmiphy */ hdmi_write_mask(hdmi_dev, HDMI_PHY_RSTOUT, ~0, HDMI_PHY_SW_RSTOUT); mdelay(10); @@ -307,6 +284,8 @@ static int hdmi_conf_apply(struct hdmi_device *hdmi_dev) /* setting core registers */ hdmi_timing_apply(hdmi_dev, conf); + hdmi_dev->cur_conf_dirty = 0; + return 0; } @@ -398,156 +377,126 @@ static void hdmi_dumpregs(struct hdmi_device *hdev, char *prefix) #undef DUMPREG } -static const struct hdmi_preset_conf hdmi_conf_480p = { - .core = { - .h_blank = {0x8a, 0x00}, - .v_blank = {0x0d, 0x6a, 0x01}, - .h_v_line = {0x0d, 0xa2, 0x35}, - .vsync_pol = {0x01}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, - .h_sync_gen = {0x0e, 0x30, 0x11}, - .v_sync_gen1 = {0x0f, 0x90, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x5a, 0x03, /* h_fsz */ - 0x8a, 0x00, 0xd0, 0x02, /* hact */ - 0x0d, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0xe0, 0x01, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, - .mbus_fmt = { - .width = 720, - .height = 480, - .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, +static const struct hdmi_timings hdmi_timings_480p = { + .hact = { .beg = 138, .end = 858 }, + .hsyn_pol = 1, + .hsyn = { .beg = 16, .end = 16 + 62 }, + .interlaced = 0, + .vact[0] = { .beg = 42 + 3, .end = 522 + 3 }, + .vsyn_pol = 1, + .vsyn[0] = { .beg = 6 + 3, .end = 12 + 3}, }; -static const struct hdmi_preset_conf hdmi_conf_720p60 = { - .core = { - .h_blank = {0x72, 0x01}, - .v_blank = {0xee, 0xf2, 0x00}, - .h_v_line = {0xee, 0x22, 0x67}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x6c, 0x50, 0x02}, - .v_sync_gen1 = {0x0a, 0x50, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x72, 0x06, /* h_fsz */ - 0x72, 0x01, 0x00, 0x05, /* hact */ - 0xee, 0x02, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x1e, 0x00, 0xd0, 0x02, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, - .mbus_fmt = { - .width = 1280, - .height = 720, - .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, +static const struct hdmi_timings hdmi_timings_576p50 = { + .hact = { .beg = 144, .end = 864 }, + .hsyn_pol = 1, + .hsyn = { .beg = 12, .end = 12 + 64 }, + .interlaced = 0, + .vact[0] = { .beg = 44 + 5, .end = 620 + 5 }, + .vsyn_pol = 1, + .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, }; -static const struct hdmi_preset_conf hdmi_conf_1080p50 = { - .core = { - .h_blank = {0xd0, 0x02}, - .v_blank = {0x65, 0x6c, 0x01}, - .h_v_line = {0x65, 0x04, 0xa5}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x0e, 0xea, 0x08}, - .v_sync_gen1 = {0x09, 0x40, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x18, 0x01, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x49, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x33, 0x02, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, - .mbus_fmt = { - .width = 1920, - .height = 1080, - .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, +static const struct hdmi_timings hdmi_timings_720p60 = { + .hact = { .beg = 370, .end = 1650 }, + .hsyn_pol = 0, + .hsyn = { .beg = 110, .end = 110 + 40 }, + .interlaced = 0, + .vact[0] = { .beg = 25 + 5, .end = 745 + 5 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, }; -static const struct hdmi_preset_conf hdmi_conf_1080p60 = { - .core = { - .h_blank = {0x18, 0x01}, - .v_blank = {0x65, 0x6c, 0x01}, - .h_v_line = {0x65, 0x84, 0x89}, - .vsync_pol = {0x00}, - .int_pro_mode = {0x00}, - .v_blank_f = {0x00, 0x00, 0x00}, /* don't care */ - .h_sync_gen = {0x56, 0x08, 0x02}, - .v_sync_gen1 = {0x09, 0x40, 0x00}, - /* other don't care */ - }, - .tg = { - 0x00, /* cmd */ - 0x98, 0x08, /* h_fsz */ - 0x18, 0x01, 0x80, 0x07, /* hact */ - 0x65, 0x04, /* v_fsz */ - 0x01, 0x00, 0x33, 0x02, /* vsync */ - 0x2d, 0x00, 0x38, 0x04, /* vact */ - 0x33, 0x02, /* field_chg */ - 0x48, 0x02, /* vact_st2 */ - 0x01, 0x00, 0x01, 0x00, /* vsync top/bot */ - 0x01, 0x00, 0x33, 0x02, /* field top/bot */ - }, - .mbus_fmt = { - .width = 1920, - .height = 1080, - .code = V4L2_MBUS_FMT_FIXED, /* means RGB888 */ - .field = V4L2_FIELD_NONE, - .colorspace = V4L2_COLORSPACE_SRGB, - }, +static const struct hdmi_timings hdmi_timings_720p50 = { + .hact = { .beg = 700, .end = 1980 }, + .hsyn_pol = 0, + .hsyn = { .beg = 440, .end = 440 + 40 }, + .interlaced = 0, + .vact[0] = { .beg = 25 + 5, .end = 745 + 5 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 5, .end = 5 + 5}, +}; + +static const struct hdmi_timings hdmi_timings_1080p24 = { + .hact = { .beg = 830, .end = 2750 }, + .hsyn_pol = 0, + .hsyn = { .beg = 638, .end = 638 + 44 }, + .interlaced = 0, + .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, +}; + +static const struct hdmi_timings hdmi_timings_1080p60 = { + .hact = { .beg = 280, .end = 2200 }, + .hsyn_pol = 0, + .hsyn = { .beg = 88, .end = 88 + 44 }, + .interlaced = 0, + .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, +}; + +static const struct hdmi_timings hdmi_timings_1080i60 = { + .hact = { .beg = 280, .end = 2200 }, + .hsyn_pol = 0, + .hsyn = { .beg = 88, .end = 88 + 44 }, + .interlaced = 1, + .vact[0] = { .beg = 20 + 2, .end = 560 + 2 }, + .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 }, + .vsyn_pol = 0, + .vsyn_off = 1100, + .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2}, + .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2}, +}; + +static const struct hdmi_timings hdmi_timings_1080i50 = { + .hact = { .beg = 720, .end = 2640 }, + .hsyn_pol = 0, + .hsyn = { .beg = 528, .end = 528 + 44 }, + .interlaced = 1, + .vact[0] = { .beg = 20 + 2, .end = 560 + 2 }, + .vact[1] = { .beg = 583 + 2, .end = 1123 + 2 }, + .vsyn_pol = 0, + .vsyn_off = 1320, + .vsyn[0] = { .beg = 0 + 2, .end = 5 + 2}, + .vsyn[1] = { .beg = 562 + 2, .end = 567 + 2}, +}; + +static const struct hdmi_timings hdmi_timings_1080p50 = { + .hact = { .beg = 720, .end = 2640 }, + .hsyn_pol = 0, + .hsyn = { .beg = 528, .end = 528 + 44 }, + .interlaced = 0, + .vact[0] = { .beg = 41 + 4, .end = 1121 + 4 }, + .vsyn_pol = 0, + .vsyn[0] = { .beg = 0 + 4, .end = 5 + 4}, }; static const struct { u32 preset; - const struct hdmi_preset_conf *conf; -} hdmi_conf[] = { - { V4L2_DV_480P59_94, &hdmi_conf_480p }, - { V4L2_DV_720P59_94, &hdmi_conf_720p60 }, - { V4L2_DV_1080P50, &hdmi_conf_1080p50 }, - { V4L2_DV_1080P30, &hdmi_conf_1080p60 }, - { V4L2_DV_1080P60, &hdmi_conf_1080p60 }, + const struct hdmi_timings *timings; +} hdmi_timings[] = { + { V4L2_DV_480P59_94, &hdmi_timings_480p }, + { V4L2_DV_576P50, &hdmi_timings_576p50 }, + { V4L2_DV_720P50, &hdmi_timings_720p50 }, + { V4L2_DV_720P59_94, &hdmi_timings_720p60 }, + { V4L2_DV_720P60, &hdmi_timings_720p60 }, + { V4L2_DV_1080P24, &hdmi_timings_1080p24 }, + { V4L2_DV_1080P30, &hdmi_timings_1080p60 }, + { V4L2_DV_1080P50, &hdmi_timings_1080p50 }, + { V4L2_DV_1080I50, &hdmi_timings_1080i50 }, + { V4L2_DV_1080I60, &hdmi_timings_1080i60 }, + { V4L2_DV_1080P60, &hdmi_timings_1080p60 }, }; -static const struct hdmi_preset_conf *hdmi_preset2conf(u32 preset) +static const struct hdmi_timings *hdmi_preset2timings(u32 preset) { int i; - for (i = 0; i < ARRAY_SIZE(hdmi_conf); ++i) - if (hdmi_conf[i].preset == preset) - return hdmi_conf[i].conf; + for (i = 0; i < ARRAY_SIZE(hdmi_timings); ++i) + if (hdmi_timings[i].preset == preset) + return hdmi_timings[i].timings; return NULL; } @@ -559,6 +508,10 @@ static int hdmi_streamon(struct hdmi_device *hdev) dev_dbg(dev, "%s\n", __func__); + ret = hdmi_conf_apply(hdev); + if (ret) + return ret; + ret = v4l2_subdev_call(hdev->phy_sd, video, s_stream, 1); if (ret) return ret; @@ -671,14 +624,15 @@ static int hdmi_s_dv_preset(struct v4l2_subdev *sd, { struct hdmi_device *hdev = sd_to_hdmi_dev(sd); struct device *dev = hdev->dev; - const struct hdmi_preset_conf *conf; + const struct hdmi_timings *conf; - conf = hdmi_preset2conf(preset->preset); + conf = hdmi_preset2timings(preset->preset); if (conf == NULL) { dev_err(dev, "preset (%u) not supported\n", preset->preset); return -EINVAL; } hdev->cur_conf = conf; + hdev->cur_conf_dirty = 1; hdev->cur_preset = preset->preset; return 0; } @@ -695,21 +649,32 @@ static int hdmi_g_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *fmt) { struct hdmi_device *hdev = sd_to_hdmi_dev(sd); - struct device *dev = hdev->dev; + const struct hdmi_timings *t = hdev->cur_conf; - dev_dbg(dev, "%s\n", __func__); + dev_dbg(hdev->dev, "%s\n", __func__); if (!hdev->cur_conf) return -EINVAL; - *fmt = hdev->cur_conf->mbus_fmt; + memset(fmt, 0, sizeof *fmt); + fmt->width = t->hact.end - t->hact.beg; + fmt->height = t->vact[0].end - t->vact[0].beg; + fmt->code = V4L2_MBUS_FMT_FIXED; /* means RGB888 */ + fmt->colorspace = V4L2_COLORSPACE_SRGB; + if (t->interlaced) { + fmt->field = V4L2_FIELD_INTERLACED; + fmt->height *= 2; + } else { + fmt->field = V4L2_FIELD_NONE; + } return 0; } static int hdmi_enum_dv_presets(struct v4l2_subdev *sd, struct v4l2_dv_enum_preset *preset) { - if (preset->index >= ARRAY_SIZE(hdmi_conf)) + if (preset->index >= ARRAY_SIZE(hdmi_timings)) return -EINVAL; - return v4l_fill_dv_preset_info(hdmi_conf[preset->index].preset, preset); + return v4l_fill_dv_preset_info(hdmi_timings[preset->index].preset, + preset); } static const struct v4l2_subdev_core_ops hdmi_sd_core_ops = { @@ -737,6 +702,8 @@ static int hdmi_runtime_suspend(struct device *dev) dev_dbg(dev, "%s\n", __func__); v4l2_subdev_call(hdev->mhl_sd, core, s_power, 0); hdmi_resource_poweroff(&hdev->res); + /* flag that device context is lost */ + hdev->cur_conf_dirty = 1; return 0; } @@ -750,10 +717,6 @@ static int hdmi_runtime_resume(struct device *dev) hdmi_resource_poweron(&hdev->res); - ret = hdmi_conf_apply(hdev); - if (ret) - goto fail; - /* starting MHL */ ret = v4l2_subdev_call(hdev->mhl_sd, core, s_power, 1); if (hdev->mhl_sd && ret) @@ -993,7 +956,8 @@ static int __devinit hdmi_probe(struct platform_device *pdev) strlcpy(sd->name, "s5p-hdmi", sizeof sd->name); hdmi_dev->cur_preset = HDMI_DEFAULT_PRESET; /* FIXME: missing fail preset is not supported */ - hdmi_dev->cur_conf = hdmi_preset2conf(hdmi_dev->cur_preset); + hdmi_dev->cur_conf = hdmi_preset2timings(hdmi_dev->cur_preset); + hdmi_dev->cur_conf_dirty = 1; /* storing subdev for call that have only access to struct device */ dev_set_drvdata(dev, sd); diff --git a/drivers/media/video/s5p-tv/hdmiphy_drv.c b/drivers/media/video/s5p-tv/hdmiphy_drv.c index 0afef77747e5..f67b38631801 100644 --- a/drivers/media/video/s5p-tv/hdmiphy_drv.c +++ b/drivers/media/video/s5p-tv/hdmiphy_drv.c @@ -26,53 +26,188 @@ MODULE_DESCRIPTION("Samsung HDMI Physical interface driver"); MODULE_LICENSE("GPL"); struct hdmiphy_conf { - u32 preset; + unsigned long pixclk; const u8 *data; }; -static const u8 hdmiphy_conf27[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, - 0x6B, 0x10, 0x02, 0x51, 0xDf, 0xF2, 0x54, 0x87, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xe3, 0x26, 0x00, 0x00, 0x00, 0x00, +struct hdmiphy_ctx { + struct v4l2_subdev sd; + const struct hdmiphy_conf *conf_tab; }; -static const u8 hdmiphy_conf74_175[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xef, 0x5B, - 0x6D, 0x10, 0x01, 0x51, 0xef, 0xF3, 0x54, 0xb9, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xa5, 0x26, 0x01, 0x00, 0x00, 0x00, +static const struct hdmiphy_conf hdmiphy_conf_s5pv210[] = { + { .pixclk = 27000000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, + 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, } + }, + { .pixclk = 27027000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, + 0x6B, 0x10, 0x02, 0x52, 0xDF, 0xF2, 0x54, 0x87, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, } + }, + { .pixclk = 74176000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, + 0x6D, 0x10, 0x01, 0x52, 0xEF, 0xF3, 0x54, 0xB9, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, } + }, + { .pixclk = 74250000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, + 0x6A, 0x10, 0x01, 0x52, 0xFF, 0xF1, 0x54, 0xBA, + 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, } + }, + { /* end marker */ } }; -static const u8 hdmiphy_conf74_25[32] = { - 0x01, 0x05, 0x00, 0xd8, 0x10, 0x9c, 0xf8, 0x40, - 0x6a, 0x10, 0x01, 0x51, 0xff, 0xf1, 0x54, 0xba, - 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xe0, - 0x22, 0x40, 0xa4, 0x26, 0x01, 0x00, 0x00, 0x00, +static const struct hdmiphy_conf hdmiphy_conf_exynos4210[] = { + { .pixclk = 27000000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x1C, 0x30, 0x40, + 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xE3, 0x26, 0x00, 0x00, 0x00, 0x00, } + }, + { .pixclk = 27027000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD4, 0x10, 0x9C, 0x09, 0x64, + 0x6B, 0x10, 0x02, 0x51, 0xDF, 0xF2, 0x54, 0x87, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xE2, 0x26, 0x00, 0x00, 0x00, 0x00, } + }, + { .pixclk = 74176000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, + 0x6D, 0x10, 0x01, 0x51, 0xEF, 0xF3, 0x54, 0xB9, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xA5, 0x26, 0x01, 0x00, 0x00, 0x00, } + }, + { .pixclk = 74250000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, + 0x6A, 0x10, 0x01, 0x51, 0xFF, 0xF1, 0x54, 0xBA, + 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x22, 0x40, 0xA4, 0x26, 0x01, 0x00, 0x00, 0x00, } + }, + { .pixclk = 148352000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xEF, 0x5B, + 0x6D, 0x18, 0x00, 0x51, 0xEF, 0xF3, 0x54, 0xB9, + 0x84, 0x00, 0x30, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x11, 0x40, 0xA5, 0x26, 0x02, 0x00, 0x00, 0x00, } + }, + { .pixclk = 148500000, .data = (u8 [32]) { + 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xF8, 0x40, + 0x6A, 0x18, 0x00, 0x51, 0xFF, 0xF1, 0x54, 0xBA, + 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, + 0x11, 0x40, 0xA4, 0x26, 0x02, 0x00, 0x00, 0x00, } + }, + { /* end marker */ } }; -static const u8 hdmiphy_conf148_5[32] = { - 0x01, 0x05, 0x00, 0xD8, 0x10, 0x9C, 0xf8, 0x40, - 0x6A, 0x18, 0x00, 0x51, 0xff, 0xF1, 0x54, 0xba, - 0x84, 0x00, 0x10, 0x38, 0x00, 0x08, 0x10, 0xE0, - 0x22, 0x40, 0xa4, 0x26, 0x02, 0x00, 0x00, 0x00, +static const struct hdmiphy_conf hdmiphy_conf_exynos4212[] = { + { .pixclk = 27000000, .data = (u8 [32]) { + 0x01, 0x11, 0x2D, 0x75, 0x00, 0x01, 0x00, 0x08, + 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71, + 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 27027000, .data = (u8 [32]) { + 0x01, 0x91, 0x2D, 0x72, 0x00, 0x64, 0x12, 0x08, + 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x71, + 0x54, 0xE2, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 74176000, .data = (u8 [32]) { + 0x01, 0x91, 0x3E, 0x35, 0x00, 0x5B, 0xDE, 0x08, + 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52, + 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 74250000, .data = (u8 [32]) { + 0x01, 0x91, 0x3E, 0x35, 0x00, 0x40, 0xF0, 0x08, + 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0x52, + 0x54, 0xA4, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 148500000, .data = (u8 [32]) { + 0x01, 0x91, 0x3E, 0x15, 0x00, 0x40, 0xF0, 0x08, + 0x82, 0x20, 0x73, 0xD9, 0x45, 0xA0, 0x34, 0xC0, + 0x0B, 0x80, 0x12, 0x87, 0x08, 0x24, 0x24, 0xA4, + 0x54, 0x4A, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, } + }, + { /* end marker */ } }; -static const struct hdmiphy_conf hdmiphy_conf[] = { - { V4L2_DV_480P59_94, hdmiphy_conf27 }, - { V4L2_DV_1080P30, hdmiphy_conf74_175 }, - { V4L2_DV_720P59_94, hdmiphy_conf74_175 }, - { V4L2_DV_720P60, hdmiphy_conf74_25 }, - { V4L2_DV_1080P50, hdmiphy_conf148_5 }, - { V4L2_DV_1080P60, hdmiphy_conf148_5 }, +static const struct hdmiphy_conf hdmiphy_conf_exynos4412[] = { + { .pixclk = 27000000, .data = (u8 [32]) { + 0x01, 0x11, 0x2D, 0x75, 0x40, 0x01, 0x00, 0x08, + 0x82, 0x00, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xE4, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 27027000, .data = (u8 [32]) { + 0x01, 0x91, 0x2D, 0x72, 0x40, 0x64, 0x12, 0x08, + 0x43, 0x20, 0x0E, 0xD9, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xE3, 0x24, 0x00, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 74176000, .data = (u8 [32]) { + 0x01, 0x91, 0x1F, 0x10, 0x40, 0x5B, 0xEF, 0x08, + 0x81, 0x20, 0xB9, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xA6, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 74250000, .data = (u8 [32]) { + 0x01, 0x91, 0x1F, 0x10, 0x40, 0x40, 0xF8, 0x08, + 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0xA5, 0x24, 0x01, 0x00, 0x00, 0x01, 0x00, } + }, + { .pixclk = 148500000, .data = (u8 [32]) { + 0x01, 0x91, 0x1F, 0x00, 0x40, 0x40, 0xF8, 0x08, + 0x81, 0x20, 0xBA, 0xD8, 0x45, 0xA0, 0xAC, 0x80, + 0x08, 0x80, 0x11, 0x84, 0x02, 0x22, 0x44, 0x86, + 0x54, 0x4B, 0x25, 0x03, 0x00, 0x00, 0x01, 0x00, } + }, + { /* end marker */ } }; -const u8 *hdmiphy_preset2conf(u32 preset) +static inline struct hdmiphy_ctx *sd_to_ctx(struct v4l2_subdev *sd) { - int i; - for (i = 0; i < ARRAY_SIZE(hdmiphy_conf); ++i) - if (hdmiphy_conf[i].preset == preset) - return hdmiphy_conf[i].data; + return container_of(sd, struct hdmiphy_ctx, sd); +} + +static unsigned long hdmiphy_preset_to_pixclk(u32 preset) +{ + static const unsigned long pixclk[] = { + [V4L2_DV_480P59_94] = 27000000, + [V4L2_DV_576P50] = 27000000, + [V4L2_DV_720P59_94] = 74176000, + [V4L2_DV_720P50] = 74250000, + [V4L2_DV_720P60] = 74250000, + [V4L2_DV_1080P24] = 74250000, + [V4L2_DV_1080P30] = 74250000, + [V4L2_DV_1080I50] = 74250000, + [V4L2_DV_1080I60] = 74250000, + [V4L2_DV_1080P50] = 148500000, + [V4L2_DV_1080P60] = 148500000, + }; + if (preset < ARRAY_SIZE(pixclk)) + return pixclk[preset]; + else + return 0; +} + +static const u8 *hdmiphy_find_conf(u32 preset, const struct hdmiphy_conf *conf) +{ + unsigned long pixclk; + + pixclk = hdmiphy_preset_to_pixclk(preset); + if (!pixclk) + return NULL; + + for (; conf->pixclk; ++conf) + if (conf->pixclk == pixclk) + return conf->data; return NULL; } @@ -88,11 +223,12 @@ static int hdmiphy_s_dv_preset(struct v4l2_subdev *sd, const u8 *data; u8 buffer[32]; int ret; + struct hdmiphy_ctx *ctx = sd_to_ctx(sd); struct i2c_client *client = v4l2_get_subdevdata(sd); struct device *dev = &client->dev; dev_info(dev, "s_dv_preset(preset = %d)\n", preset->preset); - data = hdmiphy_preset2conf(preset->preset); + data = hdmiphy_find_conf(preset->preset, ctx->conf_tab); if (!data) { dev_err(dev, "format not supported\n"); return -EINVAL; @@ -146,21 +282,36 @@ static const struct v4l2_subdev_ops hdmiphy_ops = { static int __devinit hdmiphy_probe(struct i2c_client *client, const struct i2c_device_id *id) { - static struct v4l2_subdev sd; + struct hdmiphy_ctx *ctx; + + ctx = kzalloc(sizeof *ctx, GFP_KERNEL); + if (!ctx) + return -ENOMEM; + + ctx->conf_tab = (struct hdmiphy_conf *)id->driver_data; + v4l2_i2c_subdev_init(&ctx->sd, client, &hdmiphy_ops); - v4l2_i2c_subdev_init(&sd, client, &hdmiphy_ops); dev_info(&client->dev, "probe successful\n"); return 0; } static int __devexit hdmiphy_remove(struct i2c_client *client) { + struct v4l2_subdev *sd = i2c_get_clientdata(client); + struct hdmiphy_ctx *ctx = sd_to_ctx(sd); + + kfree(ctx); dev_info(&client->dev, "remove successful\n"); + return 0; } static const struct i2c_device_id hdmiphy_id[] = { - { "hdmiphy", 0 }, + { "hdmiphy", (unsigned long)hdmiphy_conf_exynos4210 }, + { "hdmiphy-s5pv210", (unsigned long)hdmiphy_conf_s5pv210 }, + { "hdmiphy-exynos4210", (unsigned long)hdmiphy_conf_exynos4210 }, + { "hdmiphy-exynos4212", (unsigned long)hdmiphy_conf_exynos4212 }, + { "hdmiphy-exynos4412", (unsigned long)hdmiphy_conf_exynos4412 }, { }, }; MODULE_DEVICE_TABLE(i2c, hdmiphy_id); diff --git a/drivers/media/video/s5p-tv/mixer.h b/drivers/media/video/s5p-tv/mixer.h index 1597078c4a50..ddb422e23550 100644 --- a/drivers/media/video/s5p-tv/mixer.h +++ b/drivers/media/video/s5p-tv/mixer.h @@ -226,6 +226,7 @@ struct mxr_resources { /* event flags used */ enum mxr_devide_flags { MXR_EVENT_VSYNC = 0, + MXR_EVENT_TOP = 1, }; /** drivers instance */ @@ -293,7 +294,7 @@ int __devinit mxr_acquire_video(struct mxr_device *mdev, struct mxr_output_conf *output_cont, int output_count); /** releasing common video resources */ -void __devexit mxr_release_video(struct mxr_device *mdev); +void mxr_release_video(struct mxr_device *mdev); struct mxr_layer *mxr_graph_layer_create(struct mxr_device *mdev, int idx); struct mxr_layer *mxr_vp_layer_create(struct mxr_device *mdev, int idx); diff --git a/drivers/media/video/s5p-tv/mixer_drv.c b/drivers/media/video/s5p-tv/mixer_drv.c index a2c0c25ad130..edca06592883 100644 --- a/drivers/media/video/s5p-tv/mixer_drv.c +++ b/drivers/media/video/s5p-tv/mixer_drv.c @@ -461,7 +461,7 @@ static struct platform_driver mxr_driver __refdata = { static int __init mxr_init(void) { int i, ret; - static const char banner[] __initdata = KERN_INFO + static const char banner[] __initconst = KERN_INFO "Samsung TV Mixer driver, " "(c) 2010-2011 Samsung Electronics Co., Ltd.\n"; printk(banner); diff --git a/drivers/media/video/s5p-tv/mixer_reg.c b/drivers/media/video/s5p-tv/mixer_reg.c index 4800a3cbb297..3b1670a045f4 100644 --- a/drivers/media/video/s5p-tv/mixer_reg.c +++ b/drivers/media/video/s5p-tv/mixer_reg.c @@ -296,21 +296,25 @@ irqreturn_t mxr_irq_handler(int irq, void *dev_data) /* wake up process waiting for VSYNC */ if (val & MXR_INT_STATUS_VSYNC) { set_bit(MXR_EVENT_VSYNC, &mdev->event_flags); + /* toggle TOP field event if working in interlaced mode */ + if (~mxr_read(mdev, MXR_CFG) & MXR_CFG_SCAN_PROGRASSIVE) + change_bit(MXR_EVENT_TOP, &mdev->event_flags); wake_up(&mdev->event_queue); + /* vsync interrupt use different bit for read and clear */ + val &= ~MXR_INT_STATUS_VSYNC; + val |= MXR_INT_CLEAR_VSYNC; } /* clear interrupts */ - if (~val & MXR_INT_EN_VSYNC) { - /* vsync interrupt use different bit for read and clear */ - val &= ~MXR_INT_EN_VSYNC; - val |= MXR_INT_CLEAR_VSYNC; - } mxr_write(mdev, MXR_INT_STATUS, val); spin_unlock(&mdev->reg_slock); /* leave on non-vsync event */ if (~val & MXR_INT_CLEAR_VSYNC) return IRQ_HANDLED; + /* skip layer update on bottom field */ + if (!test_bit(MXR_EVENT_TOP, &mdev->event_flags)) + return IRQ_HANDLED; for (i = 0; i < MXR_MAX_LAYERS; ++i) mxr_irq_layer_handle(mdev->layer[i]); return IRQ_HANDLED; @@ -333,6 +337,7 @@ void mxr_reg_streamon(struct mxr_device *mdev) /* start MIXER */ mxr_write_mask(mdev, MXR_STATUS, ~0, MXR_STATUS_REG_RUN); + set_bit(MXR_EVENT_TOP, &mdev->event_flags); spin_unlock_irqrestore(&mdev->reg_slock, flags); } diff --git a/drivers/media/video/s5p-tv/mixer_video.c b/drivers/media/video/s5p-tv/mixer_video.c index f7ca5cc143c6..33fde2a763ec 100644 --- a/drivers/media/video/s5p-tv/mixer_video.c +++ b/drivers/media/video/s5p-tv/mixer_video.c @@ -140,7 +140,7 @@ fail: return ret; } -void __devexit mxr_release_video(struct mxr_device *mdev) +void mxr_release_video(struct mxr_device *mdev) { int i; @@ -853,8 +853,8 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *pfmt, *nplanes = fmt->num_subframes; for (i = 0; i < fmt->num_subframes; ++i) { alloc_ctxs[i] = layer->mdev->alloc_ctx; - sizes[i] = PAGE_ALIGN(planes[i].sizeimage); - mxr_dbg(mdev, "size[%d] = %08lx\n", i, sizes[i]); + sizes[i] = planes[i].sizeimage; + mxr_dbg(mdev, "size[%d] = %08x\n", i, sizes[i]); } if (*nbuffers == 0) @@ -1069,6 +1069,10 @@ struct mxr_layer *mxr_base_layer_create(struct mxr_device *mdev, set_bit(V4L2_FL_USE_FH_PRIO, &layer->vfd.flags); video_set_drvdata(&layer->vfd, layer); + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &layer->vfd.flags); layer->vfd.lock = &layer->mutex; layer->vfd.v4l2_dev = &mdev->v4l2_dev; diff --git a/drivers/media/video/s5p-tv/regs-hdmi.h b/drivers/media/video/s5p-tv/regs-hdmi.h index 33247d13e4c0..a889d1f57f28 100644 --- a/drivers/media/video/s5p-tv/regs-hdmi.h +++ b/drivers/media/video/s5p-tv/regs-hdmi.h @@ -140,6 +140,7 @@ #define HDMI_MODE_MASK (3 << 0) /* HDMI_TG_CMD */ +#define HDMI_TG_FIELD_EN (1 << 1) #define HDMI_TG_EN (1 << 0) #endif /* SAMSUNG_REGS_HDMI_H */ diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c index 53aae5968ffb..bc08f1dbc293 100644 --- a/drivers/media/video/saa7134/saa7134-cards.c +++ b/drivers/media/video/saa7134/saa7134-cards.c @@ -5080,6 +5080,36 @@ struct saa7134_board saa7134_boards[] = { .gpio = 0x0200000, }, }, + [SAA7134_BOARD_ASUSTeK_PS3_100] = { + .name = "Asus My Cinema PS3-100", + .audio_clock = 0x00187de7, + .tuner_type = TUNER_PHILIPS_TDA8290, + .radio_type = UNSET, + .tuner_addr = ADDR_UNSET, + .radio_addr = ADDR_UNSET, + .tuner_config = 2, + .gpiomask = 1 << 21, + .mpeg = SAA7134_MPEG_DVB, + .inputs = {{ + .name = name_tv, + .vmux = 1, + .amux = TV, + .tv = 1, + }, { + .name = name_comp, + .vmux = 0, + .amux = LINE2, + }, { + .name = name_svideo, + .vmux = 8, + .amux = LINE2, + } }, + .radio = { + .name = name_radio, + .amux = TV, + .gpio = 0x0200000, + }, + }, [SAA7134_BOARD_REAL_ANGEL_220] = { .name = "Zogis Real Angel 220", .audio_clock = 0x00187de7, @@ -6875,6 +6905,12 @@ struct pci_device_id saa7134_pci_tbl[] = { .subvendor = 0x1043, .subdevice = 0x4878, /* REV:1.02G */ .driver_data = SAA7134_BOARD_ASUSTeK_TIGER_3IN1, + }, { + .vendor = PCI_VENDOR_ID_PHILIPS, + .device = PCI_DEVICE_ID_PHILIPS_SAA7133, + .subvendor = 0x1043, + .subdevice = 0x48cd, + .driver_data = SAA7134_BOARD_ASUSTeK_PS3_100, }, { .vendor = PCI_VENDOR_ID_PHILIPS, .device = PCI_DEVICE_ID_PHILIPS_SAA7134, @@ -7347,6 +7383,7 @@ int saa7134_board_init1(struct saa7134_dev *dev) case SAA7134_BOARD_KWORLD_TERMINATOR: case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS: case SAA7134_BOARD_FLYDVBT_LR301: + case SAA7134_BOARD_ASUSTeK_PS3_100: case SAA7134_BOARD_ASUSTeK_P7131_DUAL: case SAA7134_BOARD_ASUSTeK_P7131_HYBRID_LNA: case SAA7134_BOARD_ASUSTeK_P7131_ANALOG: @@ -7811,6 +7848,14 @@ int saa7134_board_init2(struct saa7134_dev *dev) i2c_transfer(&dev->i2c_adap, &msg, 1); break; } + case SAA7134_BOARD_ASUSTeK_PS3_100: + { + u8 data[] = { 0x3c, 0x33, 0x60}; + struct i2c_msg msg = {.addr = 0x0b, .flags = 0, .buf = data, + .len = sizeof(data)}; + i2c_transfer(&dev->i2c_adap, &msg, 1); + break; + } case SAA7134_BOARD_FLYDVB_TRIO: { u8 temp = 0; diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c index aaa5c97a7216..5dfd826d734e 100644 --- a/drivers/media/video/saa7134/saa7134-dvb.c +++ b/drivers/media/video/saa7134/saa7134-dvb.c @@ -881,6 +881,20 @@ static struct tda1004x_config asus_tiger_3in1_config = { .request_firmware = philips_tda1004x_request_firmware }; +static struct tda1004x_config asus_ps3_100_config = { + .demod_address = 0x0b, + .invert = 1, + .invert_oclk = 0, + .xtal_freq = TDA10046_XTAL_16M, + .agc_config = TDA10046_AGC_TDA827X, + .gpio_config = TDA10046_GP11_I, + .if_freq = TDA10046_FREQ_045, + .i2c_gate = 0x4b, + .tuner_address = 0x61, + .antenna_switch = 1, + .request_firmware = philips_tda1004x_request_firmware +}; + /* ------------------------------------------------------------------ * special case: this card uses saa713x GPIO22 for the mode switch */ @@ -1647,6 +1661,31 @@ static int dvb_init(struct saa7134_dev *dev) &dev->i2c_adap, 0, 0) == NULL) { wprintk("%s: Asus Tiger 3in1, no lnbp21" " found!\n", __func__); + goto dettach_frontend; + } + } + } + break; + case SAA7134_BOARD_ASUSTeK_PS3_100: + if (!use_frontend) { /* terrestrial */ + if (configure_tda827x_fe(dev, &asus_ps3_100_config, + &tda827x_cfg_2) < 0) + goto dettach_frontend; + } else { /* satellite */ + fe0->dvb.frontend = dvb_attach(tda10086_attach, + &flydvbs, &dev->i2c_adap); + if (fe0->dvb.frontend) { + if (dvb_attach(tda826x_attach, + fe0->dvb.frontend, 0x60, + &dev->i2c_adap, 0) == NULL) { + wprintk("%s: Asus My Cinema PS3-100, no " + "tda826x found!\n", __func__); + goto dettach_frontend; + } + if (dvb_attach(lnbp21_attach, fe0->dvb.frontend, + &dev->i2c_adap, 0, 0) == NULL) { + wprintk("%s: Asus My Cinema PS3-100, no lnbp21" + " found!\n", __func__); goto dettach_frontend; } } diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c index 48d2878699b7..05c6e217d8a7 100644 --- a/drivers/media/video/saa7134/saa7134-input.c +++ b/drivers/media/video/saa7134/saa7134-input.c @@ -753,6 +753,13 @@ int saa7134_input_init1(struct saa7134_dev *dev) mask_keycode = 0xffff; raw_decode = true; break; + case SAA7134_BOARD_ASUSTeK_PS3_100: + ir_codes = RC_MAP_ASUS_PS3_100; + mask_keydown = 0x0040000; + mask_keyup = 0x0040000; + mask_keycode = 0xffff; + raw_decode = true; + break; case SAA7134_BOARD_ENCORE_ENLTV: case SAA7134_BOARD_ENCORE_ENLTV_FM: ir_codes = RC_MAP_ENCORE_ENLTV; diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c index 417034eb6ad2..6de10b1e7251 100644 --- a/drivers/media/video/saa7134/saa7134-video.c +++ b/drivers/media/video/saa7134/saa7134-video.c @@ -2036,7 +2036,7 @@ static int saa7134_s_tuner(struct file *file, void *priv, mode = dev->thread.mode; if (UNSET == mode) { rx = saa7134_tvaudio_getstereo(dev); - mode = saa7134_tvaudio_rx2mode(t->rxsubchans); + mode = saa7134_tvaudio_rx2mode(rx); } if (mode != t->audmode) dev->thread.mode = t->audmode; diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index f625060e6a0f..89c8333736a2 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -332,6 +332,7 @@ struct saa7134_card_ir { #define SAA7134_BOARD_BEHOLD_503FM 187 #define SAA7134_BOARD_SENSORAY811_911 188 #define SAA7134_BOARD_KWORLD_PC150U 189 +#define SAA7134_BOARD_ASUSTeK_PS3_100 190 #define SAA7134_MAXBOARDS 32 #define SAA7134_INPUT_MAX 8 diff --git a/drivers/media/video/saa7164/saa7164-vbi.c b/drivers/media/video/saa7164/saa7164-vbi.c index 273cf807401c..d8e6c8f14079 100644 --- a/drivers/media/video/saa7164/saa7164-vbi.c +++ b/drivers/media/video/saa7164/saa7164-vbi.c @@ -952,7 +952,7 @@ static int saa7164_vbi_start_streaming(struct saa7164_port *port) /* Stop the hardware, regardless */ result = saa7164_vbi_stop_port(port); - if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + if (result != SAA_OK) { printk(KERN_ERR "%s() pause/forced stop transition " "failed, res = 0x%x\n", __func__, result); } @@ -971,7 +971,7 @@ static int saa7164_vbi_start_streaming(struct saa7164_port *port) /* Stop the hardware, regardless */ result = saa7164_vbi_acquire_port(port); result = saa7164_vbi_stop_port(port); - if ((result != SAA_OK) && (result != SAA_ERR_ALREADY_STOPPED)) { + if (result != SAA_OK) { printk(KERN_ERR "%s() run/forced stop transition " "failed, res = 0x%x\n", __func__, result); } diff --git a/drivers/media/video/saa7164/saa7164.h b/drivers/media/video/saa7164/saa7164.h index 742b34103b5d..8d120e3baf70 100644 --- a/drivers/media/video/saa7164/saa7164.h +++ b/drivers/media/video/saa7164/saa7164.h @@ -611,11 +611,6 @@ extern unsigned int saa_debug; printk(KERN_WARNING "%s: " fmt, dev->name, ## arg);\ } while (0) -#define log_err(fmt, arg...)\ - do { \ - printk(KERN_ERROR "%s: " fmt, dev->name, ## arg);\ - } while (0) - #define saa7164_readl(reg) readl(dev->lmmio + ((reg) >> 2)) #define saa7164_writel(reg, value) writel((value), dev->lmmio + ((reg) >> 2)) diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c index 424dfacd263a..0baaf94db7e0 100644 --- a/drivers/media/video/sh_mobile_ceu_camera.c +++ b/drivers/media/video/sh_mobile_ceu_camera.c @@ -210,27 +210,33 @@ static int sh_mobile_ceu_videobuf_setup(struct vb2_queue *vq, struct soc_camera_device *icd = container_of(vq, struct soc_camera_device, vb2_vidq); struct soc_camera_host *ici = to_soc_camera_host(icd->parent); struct sh_mobile_ceu_dev *pcdev = ici->priv; - int bytes_per_line; - unsigned int height; if (fmt) { const struct soc_camera_format_xlate *xlate = soc_camera_xlate_by_fourcc(icd, fmt->fmt.pix.pixelformat); + unsigned int bytes_per_line; + int ret; + if (!xlate) return -EINVAL; - bytes_per_line = soc_mbus_bytes_per_line(fmt->fmt.pix.width, - xlate->host_fmt); - height = fmt->fmt.pix.height; + + ret = soc_mbus_bytes_per_line(fmt->fmt.pix.width, + xlate->host_fmt); + if (ret < 0) + return ret; + + bytes_per_line = max_t(u32, fmt->fmt.pix.bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, bytes_per_line, + fmt->fmt.pix.height); + if (ret < 0) + return ret; + + sizes[0] = max_t(u32, fmt->fmt.pix.sizeimage, ret); } else { /* Called from VIDIOC_REQBUFS or in compatibility mode */ - bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - height = icd->user_height; + sizes[0] = icd->sizeimage; } - if (bytes_per_line < 0) - return bytes_per_line; - - sizes[0] = bytes_per_line * height; alloc_ctxs[0] = pcdev->alloc_ctx; @@ -336,21 +342,15 @@ static int sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev) ceu_write(pcdev, top1, phys_addr_top); if (V4L2_FIELD_NONE != pcdev->field) { - if (planar) - phys_addr_bottom = phys_addr_top + icd->user_width; - else - phys_addr_bottom = phys_addr_top + - soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); + phys_addr_bottom = phys_addr_top + icd->bytesperline; ceu_write(pcdev, bottom1, phys_addr_bottom); } if (planar) { - phys_addr_top += icd->user_width * - icd->user_height; + phys_addr_top += icd->bytesperline * icd->user_height; ceu_write(pcdev, top2, phys_addr_top); if (V4L2_FIELD_NONE != pcdev->field) { - phys_addr_bottom = phys_addr_top + icd->user_width; + phys_addr_bottom = phys_addr_top + icd->bytesperline; ceu_write(pcdev, bottom2, phys_addr_bottom); } } @@ -377,13 +377,8 @@ static void sh_mobile_ceu_videobuf_queue(struct vb2_buffer *vb) struct sh_mobile_ceu_dev *pcdev = ici->priv; struct sh_mobile_ceu_buffer *buf = to_ceu_vb(vb); unsigned long size; - int bytes_per_line = soc_mbus_bytes_per_line(icd->user_width, - icd->current_fmt->host_fmt); - if (bytes_per_line < 0) - goto error; - - size = icd->user_height * bytes_per_line; + size = icd->sizeimage; if (vb2_plane_size(vb, 0) < size) { dev_err(icd->parent, "Buffer #%d too small (%lu < %lu)\n", @@ -682,10 +677,7 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) in_width *= 2; left_offset *= 2; } - cdwdr_width = width; } else { - int bytes_per_line = soc_mbus_bytes_per_line(width, - icd->current_fmt->host_fmt); unsigned int w_factor; switch (icd->current_fmt->host_fmt->packing) { @@ -698,13 +690,10 @@ static void sh_mobile_ceu_set_rect(struct soc_camera_device *icd) in_width = cam->width * w_factor; left_offset *= w_factor; - - if (bytes_per_line < 0) - cdwdr_width = width; - else - cdwdr_width = bytes_per_line; } + cdwdr_width = icd->bytesperline; + height = icd->user_height; in_height = cam->height; if (V4L2_FIELD_NONE != pcdev->field) { @@ -881,11 +870,13 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd) value |= common_flags & V4L2_MBUS_VSYNC_ACTIVE_LOW ? 1 << 1 : 0; value |= common_flags & V4L2_MBUS_HSYNC_ACTIVE_LOW ? 1 << 0 : 0; - value |= pcdev->is_16bit ? 1 << 12 : 0; - /* CSI2 mode */ - if (pcdev->pdata->csi2) + if (pcdev->pdata->csi2) /* CSI2 mode */ value |= 3 << 12; + else if (pcdev->is_16bit) + value |= 1 << 12; + else if (pcdev->pdata->flags & SH_CEU_FLAG_LOWER_8BIT) + value |= 2 << 12; ceu_write(pcdev, CAMCR, value); @@ -964,24 +955,28 @@ static const struct soc_mbus_pixelfmt sh_mobile_ceu_formats[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, }, { .fourcc = V4L2_PIX_FMT_NV21, .name = "NV21", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_2Y_C, }, { .fourcc = V4L2_PIX_FMT_NV16, .name = "NV16", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, }, { .fourcc = V4L2_PIX_FMT_NV61, .name = "NV61", .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PLANAR_Y_C, }, }; @@ -1845,6 +1840,8 @@ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd, return 0; } +#define CEU_CHDW_MAX 8188U /* Maximum line stride */ + static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { @@ -1863,8 +1860,12 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); if (!xlate) { - dev_warn(icd->parent, "Format %x not found\n", pixfmt); - return -EINVAL; + xlate = icd->current_fmt; + dev_dbg(icd->parent, "Format %x not found, keeping %x\n", + pixfmt, xlate->host_fmt->fourcc); + pixfmt = xlate->host_fmt->fourcc; + pix->pixelformat = pixfmt; + pix->colorspace = icd->colorspace; } /* FIXME: calculate using depth and bus width */ @@ -1923,10 +1924,20 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd, pix->width = width; if (mf.height > height) pix->height = height; + + pix->bytesperline = max(pix->bytesperline, pix->width); + pix->bytesperline = min(pix->bytesperline, CEU_CHDW_MAX); + pix->bytesperline &= ~3; + break; + + default: + /* Configurable stride isn't supported in pass-through mode. */ + pix->bytesperline = 0; } pix->width &= ~3; pix->height &= ~3; + pix->sizeimage = 0; dev_geo(icd->parent, "%s(): return %d, fmt 0x%x, %ux%u\n", __func__, ret, pix->pixelformat, pix->width, pix->height); @@ -2145,6 +2156,7 @@ static int __devinit sh_mobile_ceu_probe(struct platform_device *pdev) pcdev->ici.nr = pdev->id; pcdev->ici.drv_name = dev_name(&pdev->dev); pcdev->ici.ops = &sh_mobile_ceu_host_ops; + pcdev->ici.capabilities = SOCAM_HOST_CAP_STRIDE; pcdev->alloc_ctx = vb2_dma_contig_init_ctx(&pdev->dev); if (IS_ERR(pcdev->alloc_ctx)) { diff --git a/drivers/media/video/sh_vou.c b/drivers/media/video/sh_vou.c index 9644bd861abc..8fd1874382c6 100644 --- a/drivers/media/video/sh_vou.c +++ b/drivers/media/video/sh_vou.c @@ -1390,6 +1390,10 @@ static int __devinit sh_vou_probe(struct platform_device *pdev) vdev->v4l2_dev = &vou_dev->v4l2_dev; vdev->release = video_device_release; vdev->lock = &vou_dev->fop_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); vou_dev->vdev = vdev; video_set_drvdata(vdev, vou_dev); diff --git a/drivers/media/video/smiapp-pll.c b/drivers/media/video/smiapp-pll.c new file mode 100644 index 000000000000..a2e41a21dc65 --- /dev/null +++ b/drivers/media/video/smiapp-pll.c @@ -0,0 +1,418 @@ +/* + * drivers/media/video/smiapp-pll.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include + +#include "smiapp-pll.h" + +/* Return an even number or one. */ +static inline uint32_t clk_div_even(uint32_t a) +{ + return max_t(uint32_t, 1, a & ~1); +} + +/* Return an even number or one. */ +static inline uint32_t clk_div_even_up(uint32_t a) +{ + if (a == 1) + return 1; + return (a + 1) & ~1; +} + +static inline uint32_t is_one_or_even(uint32_t a) +{ + if (a == 1) + return 1; + if (a & 1) + return 0; + + return 1; +} + +static int bounds_check(struct device *dev, uint32_t val, + uint32_t min, uint32_t max, char *str) +{ + if (val >= min && val <= max) + return 0; + + dev_warn(dev, "%s out of bounds: %d (%d--%d)\n", str, val, min, max); + + return -EINVAL; +} + +static void print_pll(struct device *dev, struct smiapp_pll *pll) +{ + dev_dbg(dev, "pre_pll_clk_div\t%d\n", pll->pre_pll_clk_div); + dev_dbg(dev, "pll_multiplier \t%d\n", pll->pll_multiplier); + if (pll->flags != SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_div \t%d\n", pll->op_sys_clk_div); + dev_dbg(dev, "op_pix_clk_div \t%d\n", pll->op_pix_clk_div); + } + dev_dbg(dev, "vt_sys_clk_div \t%d\n", pll->vt_sys_clk_div); + dev_dbg(dev, "vt_pix_clk_div \t%d\n", pll->vt_pix_clk_div); + + dev_dbg(dev, "ext_clk_freq_hz \t%d\n", pll->ext_clk_freq_hz); + dev_dbg(dev, "pll_ip_clk_freq_hz \t%d\n", pll->pll_ip_clk_freq_hz); + dev_dbg(dev, "pll_op_clk_freq_hz \t%d\n", pll->pll_op_clk_freq_hz); + if (pll->flags & SMIAPP_PLL_FLAG_NO_OP_CLOCKS) { + dev_dbg(dev, "op_sys_clk_freq_hz \t%d\n", + pll->op_sys_clk_freq_hz); + dev_dbg(dev, "op_pix_clk_freq_hz \t%d\n", + pll->op_pix_clk_freq_hz); + } + dev_dbg(dev, "vt_sys_clk_freq_hz \t%d\n", pll->vt_sys_clk_freq_hz); + dev_dbg(dev, "vt_pix_clk_freq_hz \t%d\n", pll->vt_pix_clk_freq_hz); +} + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll) +{ + uint32_t sys_div; + uint32_t best_pix_div = INT_MAX >> 1; + uint32_t vt_op_binning_div; + uint32_t lane_op_clock_ratio; + uint32_t mul, div; + uint32_t more_mul_min, more_mul_max; + uint32_t more_mul_factor; + uint32_t min_vt_div, max_vt_div, vt_div; + uint32_t min_sys_div, max_sys_div; + unsigned int i; + int rval; + + if (pll->flags & SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE) + lane_op_clock_ratio = pll->lanes; + else + lane_op_clock_ratio = 1; + dev_dbg(dev, "lane_op_clock_ratio: %d\n", lane_op_clock_ratio); + + dev_dbg(dev, "binning: %dx%d\n", pll->binning_horizontal, + pll->binning_vertical); + + /* CSI transfers 2 bits per clock per lane; thus times 2 */ + pll->pll_op_clk_freq_hz = pll->link_freq * 2 + * (pll->lanes / lane_op_clock_ratio); + + /* Figure out limits for pre-pll divider based on extclk */ + dev_dbg(dev, "min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + limits->max_pre_pll_clk_div = + min_t(uint16_t, limits->max_pre_pll_clk_div, + clk_div_even(pll->ext_clk_freq_hz / + limits->min_pll_ip_freq_hz)); + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(pll->ext_clk_freq_hz, + limits->max_pll_ip_freq_hz))); + dev_dbg(dev, "pre-pll check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + i = gcd(pll->pll_op_clk_freq_hz, pll->ext_clk_freq_hz); + mul = div_u64(pll->pll_op_clk_freq_hz, i); + div = pll->ext_clk_freq_hz / i; + dev_dbg(dev, "mul %d / div %d\n", mul, div); + + limits->min_pre_pll_clk_div = + max_t(uint16_t, limits->min_pre_pll_clk_div, + clk_div_even_up( + DIV_ROUND_UP(mul * pll->ext_clk_freq_hz, + limits->max_pll_op_freq_hz))); + dev_dbg(dev, "pll_op check: min / max pre_pll_clk_div: %d / %d\n", + limits->min_pre_pll_clk_div, limits->max_pre_pll_clk_div); + + if (limits->min_pre_pll_clk_div > limits->max_pre_pll_clk_div) { + dev_err(dev, "unable to compute pre_pll divisor\n"); + return -EINVAL; + } + + pll->pre_pll_clk_div = limits->min_pre_pll_clk_div; + + /* + * Get pre_pll_clk_div so that our pll_op_clk_freq_hz won't be + * too high. + */ + dev_dbg(dev, "pre_pll_clk_div %d\n", pll->pre_pll_clk_div); + + /* Don't go above max pll multiplier. */ + more_mul_max = limits->max_pll_multiplier / mul; + dev_dbg(dev, "more_mul_max: max_pll_multiplier check: %d\n", + more_mul_max); + /* Don't go above max pll op frequency. */ + more_mul_max = + min_t(int, + more_mul_max, + limits->max_pll_op_freq_hz + / (pll->ext_clk_freq_hz / pll->pre_pll_clk_div * mul)); + dev_dbg(dev, "more_mul_max: max_pll_op_freq_hz check: %d\n", + more_mul_max); + /* Don't go above the division capability of op sys clock divider. */ + more_mul_max = min(more_mul_max, + limits->max_op_sys_clk_div * pll->pre_pll_clk_div + / div); + dev_dbg(dev, "more_mul_max: max_op_sys_clk_div check: %d\n", + more_mul_max); + /* Ensure we won't go above min_pll_multiplier. */ + more_mul_max = min(more_mul_max, + DIV_ROUND_UP(limits->max_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_max: min_pll_multiplier check: %d\n", + more_mul_max); + + /* Ensure we won't go below min_pll_op_freq_hz. */ + more_mul_min = DIV_ROUND_UP(limits->min_pll_op_freq_hz, + pll->ext_clk_freq_hz / pll->pre_pll_clk_div + * mul); + dev_dbg(dev, "more_mul_min: min_pll_op_freq_hz check: %d\n", + more_mul_min); + /* Ensure we won't go below min_pll_multiplier. */ + more_mul_min = max(more_mul_min, + DIV_ROUND_UP(limits->min_pll_multiplier, mul)); + dev_dbg(dev, "more_mul_min: min_pll_multiplier check: %d\n", + more_mul_min); + + if (more_mul_min > more_mul_max) { + dev_warn(dev, + "unable to compute more_mul_min and more_mul_max"); + return -EINVAL; + } + + more_mul_factor = lcm(div, pll->pre_pll_clk_div) / div; + dev_dbg(dev, "more_mul_factor: %d\n", more_mul_factor); + more_mul_factor = lcm(more_mul_factor, limits->min_op_sys_clk_div); + dev_dbg(dev, "more_mul_factor: min_op_sys_clk_div: %d\n", + more_mul_factor); + i = roundup(more_mul_min, more_mul_factor); + if (!is_one_or_even(i)) + i <<= 1; + + dev_dbg(dev, "final more_mul: %d\n", i); + if (i > more_mul_max) { + dev_warn(dev, "final more_mul is bad, max %d", more_mul_max); + return -EINVAL; + } + + pll->pll_multiplier = mul * i; + pll->op_sys_clk_div = div * i / pll->pre_pll_clk_div; + dev_dbg(dev, "op_sys_clk_div: %d\n", pll->op_sys_clk_div); + + pll->pll_ip_clk_freq_hz = pll->ext_clk_freq_hz + / pll->pre_pll_clk_div; + + pll->pll_op_clk_freq_hz = pll->pll_ip_clk_freq_hz + * pll->pll_multiplier; + + /* Derive pll_op_clk_freq_hz. */ + pll->op_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->op_sys_clk_div; + + pll->op_pix_clk_div = pll->bits_per_pixel; + dev_dbg(dev, "op_pix_clk_div: %d\n", pll->op_pix_clk_div); + + pll->op_pix_clk_freq_hz = + pll->op_sys_clk_freq_hz / pll->op_pix_clk_div; + + /* + * Some sensors perform analogue binning and some do this + * digitally. The ones doing this digitally can be roughly be + * found out using this formula. The ones doing this digitally + * should run at higher clock rate, so smaller divisor is used + * on video timing side. + */ + if (limits->min_line_length_pck_bin > limits->min_line_length_pck + / pll->binning_horizontal) + vt_op_binning_div = pll->binning_horizontal; + else + vt_op_binning_div = 1; + dev_dbg(dev, "vt_op_binning_div: %d\n", vt_op_binning_div); + + /* + * Profile 2 supports vt_pix_clk_div E [4, 10] + * + * Horizontal binning can be used as a base for difference in + * divisors. One must make sure that horizontal blanking is + * enough to accommodate the CSI-2 sync codes. + * + * Take scaling factor into account as well. + * + * Find absolute limits for the factor of vt divider. + */ + dev_dbg(dev, "scale_m: %d\n", pll->scale_m); + min_vt_div = DIV_ROUND_UP(pll->op_pix_clk_div * pll->op_sys_clk_div + * pll->scale_n, + lane_op_clock_ratio * vt_op_binning_div + * pll->scale_m); + + /* Find smallest and biggest allowed vt divisor. */ + dev_dbg(dev, "min_vt_div: %d\n", min_vt_div); + min_vt_div = max(min_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz)); + dev_dbg(dev, "min_vt_div: max_vt_pix_clk_freq_hz: %d\n", + min_vt_div); + min_vt_div = max_t(uint32_t, min_vt_div, + limits->min_vt_pix_clk_div + * limits->min_vt_sys_clk_div); + dev_dbg(dev, "min_vt_div: min_vt_clk_div: %d\n", min_vt_div); + + max_vt_div = limits->max_vt_sys_clk_div * limits->max_vt_pix_clk_div; + dev_dbg(dev, "max_vt_div: %d\n", max_vt_div); + max_vt_div = min(max_vt_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_vt_div: min_vt_pix_clk_freq_hz: %d\n", + max_vt_div); + + /* + * Find limitsits for sys_clk_div. Not all values are possible + * with all values of pix_clk_div. + */ + min_sys_div = limits->min_vt_sys_clk_div; + dev_dbg(dev, "min_sys_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + DIV_ROUND_UP(min_vt_div, + limits->max_vt_pix_clk_div)); + dev_dbg(dev, "min_sys_div: max_vt_pix_clk_div: %d\n", min_sys_div); + min_sys_div = max(min_sys_div, + pll->pll_op_clk_freq_hz + / limits->max_vt_sys_clk_freq_hz); + dev_dbg(dev, "min_sys_div: max_pll_op_clk_freq_hz: %d\n", min_sys_div); + min_sys_div = clk_div_even_up(min_sys_div); + dev_dbg(dev, "min_sys_div: one or even: %d\n", min_sys_div); + + max_sys_div = limits->max_vt_sys_clk_div; + dev_dbg(dev, "max_sys_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(max_vt_div, + limits->min_vt_pix_clk_div)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_div: %d\n", max_sys_div); + max_sys_div = min(max_sys_div, + DIV_ROUND_UP(pll->pll_op_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz)); + dev_dbg(dev, "max_sys_div: min_vt_pix_clk_freq_hz: %d\n", max_sys_div); + + /* + * Find pix_div such that a legal pix_div * sys_div results + * into a value which is not smaller than div, the desired + * divisor. + */ + for (vt_div = min_vt_div; vt_div <= max_vt_div; + vt_div += 2 - (vt_div & 1)) { + for (sys_div = min_sys_div; + sys_div <= max_sys_div; + sys_div += 2 - (sys_div & 1)) { + int pix_div = DIV_ROUND_UP(vt_div, sys_div); + + if (pix_div < limits->min_vt_pix_clk_div + || pix_div > limits->max_vt_pix_clk_div) { + dev_dbg(dev, + "pix_div %d too small or too big (%d--%d)\n", + pix_div, + limits->min_vt_pix_clk_div, + limits->max_vt_pix_clk_div); + continue; + } + + /* Check if this one is better. */ + if (pix_div * sys_div + <= roundup(min_vt_div, best_pix_div)) + best_pix_div = pix_div; + } + if (best_pix_div < INT_MAX >> 1) + break; + } + + pll->vt_sys_clk_div = DIV_ROUND_UP(min_vt_div, best_pix_div); + pll->vt_pix_clk_div = best_pix_div; + + pll->vt_sys_clk_freq_hz = + pll->pll_op_clk_freq_hz / pll->vt_sys_clk_div; + pll->vt_pix_clk_freq_hz = + pll->vt_sys_clk_freq_hz / pll->vt_pix_clk_div; + + pll->pixel_rate_csi = + pll->op_pix_clk_freq_hz * lane_op_clock_ratio; + + print_pll(dev, pll); + + rval = bounds_check(dev, pll->pre_pll_clk_div, + limits->min_pre_pll_clk_div, + limits->max_pre_pll_clk_div, "pre_pll_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->pll_ip_clk_freq_hz, + limits->min_pll_ip_freq_hz, limits->max_pll_ip_freq_hz, + "pll_ip_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->pll_multiplier, + limits->min_pll_multiplier, limits->max_pll_multiplier, + "pll_multiplier"); + if (!rval) + rval = bounds_check( + dev, pll->pll_op_clk_freq_hz, + limits->min_pll_op_freq_hz, limits->max_pll_op_freq_hz, + "pll_op_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_div, + limits->min_op_sys_clk_div, limits->max_op_sys_clk_div, + "op_sys_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_div, + limits->min_op_pix_clk_div, limits->max_op_pix_clk_div, + "op_pix_clk_div"); + if (!rval) + rval = bounds_check( + dev, pll->op_sys_clk_freq_hz, + limits->min_op_sys_clk_freq_hz, + limits->max_op_sys_clk_freq_hz, + "op_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->op_pix_clk_freq_hz, + limits->min_op_pix_clk_freq_hz, + limits->max_op_pix_clk_freq_hz, + "op_pix_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_sys_clk_freq_hz, + limits->min_vt_sys_clk_freq_hz, + limits->max_vt_sys_clk_freq_hz, + "vt_sys_clk_freq_hz"); + if (!rval) + rval = bounds_check( + dev, pll->vt_pix_clk_freq_hz, + limits->min_vt_pix_clk_freq_hz, + limits->max_vt_pix_clk_freq_hz, + "vt_pix_clk_freq_hz"); + + return rval; +} +EXPORT_SYMBOL_GPL(smiapp_pll_calculate); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ PLL calculator"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp-pll.h b/drivers/media/video/smiapp-pll.h new file mode 100644 index 000000000000..9eab63f23afb --- /dev/null +++ b/drivers/media/video/smiapp-pll.h @@ -0,0 +1,103 @@ +/* + * drivers/media/video/smiapp-pll.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_PLL_H +#define SMIAPP_PLL_H + +#include + +struct smiapp_pll { + uint8_t lanes; + uint8_t binning_horizontal; + uint8_t binning_vertical; + uint8_t scale_m; + uint8_t scale_n; + uint8_t bits_per_pixel; + uint16_t flags; + uint32_t link_freq; + + uint16_t pre_pll_clk_div; + uint16_t pll_multiplier; + uint16_t op_sys_clk_div; + uint16_t op_pix_clk_div; + uint16_t vt_sys_clk_div; + uint16_t vt_pix_clk_div; + + uint32_t ext_clk_freq_hz; + uint32_t pll_ip_clk_freq_hz; + uint32_t pll_op_clk_freq_hz; + uint32_t op_sys_clk_freq_hz; + uint32_t op_pix_clk_freq_hz; + uint32_t vt_sys_clk_freq_hz; + uint32_t vt_pix_clk_freq_hz; + + uint32_t pixel_rate_csi; +}; + +struct smiapp_pll_limits { + /* Strict PLL limits */ + uint32_t min_ext_clk_freq_hz; + uint32_t max_ext_clk_freq_hz; + uint16_t min_pre_pll_clk_div; + uint16_t max_pre_pll_clk_div; + uint32_t min_pll_ip_freq_hz; + uint32_t max_pll_ip_freq_hz; + uint16_t min_pll_multiplier; + uint16_t max_pll_multiplier; + uint32_t min_pll_op_freq_hz; + uint32_t max_pll_op_freq_hz; + + uint16_t min_vt_sys_clk_div; + uint16_t max_vt_sys_clk_div; + uint32_t min_vt_sys_clk_freq_hz; + uint32_t max_vt_sys_clk_freq_hz; + uint16_t min_vt_pix_clk_div; + uint16_t max_vt_pix_clk_div; + uint32_t min_vt_pix_clk_freq_hz; + uint32_t max_vt_pix_clk_freq_hz; + + uint16_t min_op_sys_clk_div; + uint16_t max_op_sys_clk_div; + uint32_t min_op_sys_clk_freq_hz; + uint32_t max_op_sys_clk_freq_hz; + uint16_t min_op_pix_clk_div; + uint16_t max_op_pix_clk_div; + uint32_t min_op_pix_clk_freq_hz; + uint32_t max_op_pix_clk_freq_hz; + + /* Other relevant limits */ + uint32_t min_line_length_pck_bin; + uint32_t min_line_length_pck; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_PLL_FLAG_NO_OP_CLOCKS (1 << 1) + +struct device; + +int smiapp_pll_calculate(struct device *dev, struct smiapp_pll_limits *limits, + struct smiapp_pll *pll); + +#endif /* SMIAPP_PLL_H */ diff --git a/drivers/media/video/smiapp/Kconfig b/drivers/media/video/smiapp/Kconfig new file mode 100644 index 000000000000..f7b35ff443bf --- /dev/null +++ b/drivers/media/video/smiapp/Kconfig @@ -0,0 +1,6 @@ +config VIDEO_SMIAPP + tristate "SMIA++/SMIA sensor support" + depends on I2C && VIDEO_V4L2 && VIDEO_V4L2_SUBDEV_API + select VIDEO_SMIAPP_PLL + ---help--- + This is a generic driver for SMIA++/SMIA camera modules. diff --git a/drivers/media/video/smiapp/Makefile b/drivers/media/video/smiapp/Makefile new file mode 100644 index 000000000000..36b0cfa2c541 --- /dev/null +++ b/drivers/media/video/smiapp/Makefile @@ -0,0 +1,5 @@ +smiapp-objs += smiapp-core.o smiapp-regs.o \ + smiapp-quirk.o smiapp-limits.o +obj-$(CONFIG_VIDEO_SMIAPP) += smiapp.o + +ccflags-y += -Idrivers/media/video diff --git a/drivers/media/video/smiapp/smiapp-core.c b/drivers/media/video/smiapp/smiapp-core.c new file mode 100644 index 000000000000..f518026cb67b --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-core.c @@ -0,0 +1,2894 @@ +/* + * drivers/media/video/smiapp/smiapp-core.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * Based on smiapp driver by Vimarsh Zutshi + * Based on jt8ev1.c by Vimarsh Zutshi + * Based on smia-sensor.c by Tuukka Toivonen + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "smiapp.h" + +#define SMIAPP_ALIGN_DIM(dim, flags) \ + ((flags) & V4L2_SUBDEV_SEL_FLAG_SIZE_GE \ + ? ALIGN((dim), 2) \ + : (dim) & ~1) + +/* + * smiapp_module_idents - supported camera modules + */ +static const struct smiapp_module_ident smiapp_module_idents[] = { + SMIAPP_IDENT_L(0x01, 0x022b, -1, "vs6555"), + SMIAPP_IDENT_L(0x01, 0x022e, -1, "vw6558"), + SMIAPP_IDENT_L(0x07, 0x7698, -1, "ovm7698"), + SMIAPP_IDENT_L(0x0b, 0x4242, -1, "smiapp-003"), + SMIAPP_IDENT_L(0x0c, 0x208a, -1, "tcm8330md"), + SMIAPP_IDENT_LQ(0x0c, 0x2134, -1, "tcm8500md", &smiapp_tcm8500md_quirk), + SMIAPP_IDENT_L(0x0c, 0x213e, -1, "et8en2"), + SMIAPP_IDENT_L(0x0c, 0x2184, -1, "tcm8580md"), + SMIAPP_IDENT_LQ(0x0c, 0x560f, -1, "jt8ew9", &smiapp_jt8ew9_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4141, -1, "jt8ev1", &smiapp_jt8ev1_quirk), + SMIAPP_IDENT_LQ(0x10, 0x4241, -1, "imx125es", &smiapp_imx125es_quirk), +}; + +/* + * + * Dynamic Capability Identification + * + */ + +static int smiapp_read_frame_fmt(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + u32 fmt_model_type, fmt_model_subtype, ncol_desc, nrow_desc; + unsigned int i; + int rval; + int line_count = 0; + int embedded_start = -1, embedded_end = -1; + int image_start = 0; + + rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE, + &fmt_model_type); + if (rval) + return rval; + + rval = smiapp_read(sensor, SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE, + &fmt_model_subtype); + if (rval) + return rval; + + ncol_desc = (fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK) + >> SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT; + nrow_desc = fmt_model_subtype + & SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK; + + dev_dbg(&client->dev, "format_model_type %s\n", + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE + ? "2 byte" : + fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE + ? "4 byte" : "is simply bad"); + + for (i = 0; i < ncol_desc + nrow_desc; i++) { + u32 desc; + u32 pixelcode; + u32 pixels; + char *which; + char *what; + + if (fmt_model_type == SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK; + } else if (fmt_model_type + == SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(i), + &desc); + if (rval) + return rval; + + pixelcode = + (desc + & SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK) + >> SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT; + pixels = desc & SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK; + } else { + dev_dbg(&client->dev, + "invalid frame format model type %d\n", + fmt_model_type); + return -EINVAL; + } + + if (i < ncol_desc) + which = "columns"; + else + which = "rows"; + + switch (pixelcode) { + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED: + what = "embedded"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY: + what = "dummy"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK: + what = "black"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK: + what = "dark"; + break; + case SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE: + what = "visible"; + break; + default: + what = "invalid"; + dev_dbg(&client->dev, "pixelcode %d\n", pixelcode); + break; + } + + dev_dbg(&client->dev, "%s pixels: %d %s\n", + what, pixels, which); + + if (i < ncol_desc) + continue; + + /* Handle row descriptors */ + if (pixelcode + == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED) { + embedded_start = line_count; + } else { + if (pixelcode == SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE + || pixels >= sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES] / 2) + image_start = line_count; + if (embedded_start != -1 && embedded_end == -1) + embedded_end = line_count; + } + line_count += pixels; + } + + if (embedded_start == -1 || embedded_end == -1) { + embedded_start = 0; + embedded_end = 0; + } + + dev_dbg(&client->dev, "embedded data from lines %d to %d\n", + embedded_start, embedded_end); + dev_dbg(&client->dev, "image data starts at line %d\n", image_start); + + return 0; +} + +static int smiapp_pll_configure(struct smiapp_sensor *sensor) +{ + struct smiapp_pll *pll = &sensor->pll; + int rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_VT_PIX_CLK_DIV, pll->vt_pix_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_VT_SYS_CLK_DIV, pll->vt_sys_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_PRE_PLL_CLK_DIV, pll->pre_pll_clk_div); + if (rval < 0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_PLL_MULTIPLIER, pll->pll_multiplier); + if (rval < 0) + return rval; + + /* Lane op clock ratio does not apply here. */ + rval = smiapp_write( + sensor, SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS, + DIV_ROUND_UP(pll->op_sys_clk_freq_hz, 1000000 / 256 / 256)); + if (rval < 0 || sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) + return rval; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_OP_PIX_CLK_DIV, pll->op_pix_clk_div); + if (rval < 0) + return rval; + + return smiapp_write( + sensor, SMIAPP_REG_U16_OP_SYS_CLK_DIV, pll->op_sys_clk_div); +} + +static int smiapp_pll_update(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct smiapp_pll_limits lim = { + .min_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV], + .max_pre_pll_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV], + .min_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ], + .max_pll_ip_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ], + .min_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MIN_PLL_MULTIPLIER], + .max_pll_multiplier = sensor->limits[SMIAPP_LIMIT_MAX_PLL_MULTIPLIER], + .min_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ], + .max_pll_op_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ], + + .min_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV], + .max_op_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV], + .min_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV], + .max_op_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV], + .min_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ], + .max_op_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ], + .min_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ], + .max_op_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ], + + .min_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV], + .max_vt_sys_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV], + .min_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV], + .max_vt_pix_clk_div = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV], + .min_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ], + .max_vt_sys_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ], + .min_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ], + .max_vt_pix_clk_freq_hz = sensor->limits[SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ], + + .min_line_length_pck_bin = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN], + .min_line_length_pck = sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK], + }; + struct smiapp_pll *pll = &sensor->pll; + int rval; + + memset(&sensor->pll, 0, sizeof(sensor->pll)); + + pll->lanes = sensor->platform_data->lanes; + pll->ext_clk_freq_hz = sensor->platform_data->ext_clk; + + if (sensor->minfo.smiapp_profile == SMIAPP_PROFILE_0) { + /* + * Fill in operational clock divisors limits from the + * video timing ones. On profile 0 sensors the + * requirements regarding them are essentially the + * same as on VT ones. + */ + lim.min_op_sys_clk_div = lim.min_vt_sys_clk_div; + lim.max_op_sys_clk_div = lim.max_vt_sys_clk_div; + lim.min_op_pix_clk_div = lim.min_vt_pix_clk_div; + lim.max_op_pix_clk_div = lim.max_vt_pix_clk_div; + lim.min_op_sys_clk_freq_hz = lim.min_vt_sys_clk_freq_hz; + lim.max_op_sys_clk_freq_hz = lim.max_vt_sys_clk_freq_hz; + lim.min_op_pix_clk_freq_hz = lim.min_vt_pix_clk_freq_hz; + lim.max_op_pix_clk_freq_hz = lim.max_vt_pix_clk_freq_hz; + /* Profile 0 sensors have no separate OP clock branch. */ + pll->flags |= SMIAPP_PLL_FLAG_NO_OP_CLOCKS; + } + + if (smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE)) + pll->flags |= SMIAPP_PLL_FLAG_OP_PIX_CLOCK_PER_LANE; + + pll->binning_horizontal = sensor->binning_horizontal; + pll->binning_vertical = sensor->binning_vertical; + pll->link_freq = + sensor->link_freq->qmenu_int[sensor->link_freq->val]; + pll->scale_m = sensor->scale_m; + pll->scale_n = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + pll->bits_per_pixel = sensor->csi_format->compressed; + + rval = smiapp_pll_calculate(&client->dev, &lim, pll); + if (rval < 0) + return rval; + + sensor->pixel_rate_parray->cur.val64 = pll->vt_pix_clk_freq_hz; + sensor->pixel_rate_csi->cur.val64 = pll->pixel_rate_csi; + + return 0; +} + + +/* + * + * V4L2 Controls handling + * + */ + +static void __smiapp_update_exposure_limits(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *ctrl = sensor->exposure; + int max; + + max = sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val + - sensor->limits[SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN]; + + ctrl->maximum = max; + if (ctrl->default_value > max) + ctrl->default_value = max; + if (ctrl->val > max) + ctrl->val = max; + if (ctrl->cur.val > max) + ctrl->cur.val = max; +} + +/* + * Order matters. + * + * 1. Bits-per-pixel, descending. + * 2. Bits-per-pixel compressed, descending. + * 3. Pixel order, same as in pixel_order_str. Formats for all four pixel + * orders must be defined. + */ +static const struct smiapp_csi_data_format smiapp_csi_data_formats[] = { + { V4L2_MBUS_FMT_SGRBG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG12_1X12, 12, 12, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_1X10, 10, 10, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG10_DPCM8_1X8, 10, 8, SMIAPP_PIXEL_ORDER_GBRG, }, + { V4L2_MBUS_FMT_SGRBG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GRBG, }, + { V4L2_MBUS_FMT_SRGGB8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_RGGB, }, + { V4L2_MBUS_FMT_SBGGR8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_BGGR, }, + { V4L2_MBUS_FMT_SGBRG8_1X8, 8, 8, SMIAPP_PIXEL_ORDER_GBRG, }, +}; + +const char *pixel_order_str[] = { "GRBG", "RGGB", "BGGR", "GBRG" }; + +#define to_csi_format_idx(fmt) (((unsigned long)(fmt) \ + - (unsigned long)smiapp_csi_data_formats) \ + / sizeof(*smiapp_csi_data_formats)) + +static u32 smiapp_pixel_order(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int flip = 0; + + if (sensor->hflip) { + if (sensor->hflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + flip |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + } + + flip ^= sensor->hvflip_inv_mask; + + dev_dbg(&client->dev, "flip %d\n", flip); + return sensor->default_pixel_order ^ flip; +} + +static void smiapp_update_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int csi_format_idx = + to_csi_format_idx(sensor->csi_format) & ~3; + unsigned int internal_csi_format_idx = + to_csi_format_idx(sensor->internal_csi_format) & ~3; + unsigned int pixel_order = smiapp_pixel_order(sensor); + + sensor->mbus_frame_fmts = + sensor->default_mbus_frame_fmts << pixel_order; + sensor->csi_format = + &smiapp_csi_data_formats[csi_format_idx + pixel_order]; + sensor->internal_csi_format = + &smiapp_csi_data_formats[internal_csi_format_idx + + pixel_order]; + + BUG_ON(max(internal_csi_format_idx, csi_format_idx) + pixel_order + >= ARRAY_SIZE(smiapp_csi_data_formats)); + BUG_ON(min(internal_csi_format_idx, csi_format_idx) < 0); + + dev_dbg(&client->dev, "new pixel order %s\n", + pixel_order_str[pixel_order]); +} + +static int smiapp_set_ctrl(struct v4l2_ctrl *ctrl) +{ + struct smiapp_sensor *sensor = + container_of(ctrl->handler, struct smiapp_subdev, ctrl_handler) + ->sensor; + u32 orient = 0; + int exposure; + int rval; + + switch (ctrl->id) { + case V4L2_CID_ANALOGUE_GAIN: + return smiapp_write( + sensor, + SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL, ctrl->val); + + case V4L2_CID_EXPOSURE: + return smiapp_write( + sensor, + SMIAPP_REG_U16_COARSE_INTEGRATION_TIME, ctrl->val); + + case V4L2_CID_HFLIP: + case V4L2_CID_VFLIP: + if (sensor->streaming) + return -EBUSY; + + if (sensor->hflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_HFLIP; + + if (sensor->vflip->val) + orient |= SMIAPP_IMAGE_ORIENTATION_VFLIP; + + orient ^= sensor->hvflip_inv_mask; + rval = smiapp_write(sensor, + SMIAPP_REG_U8_IMAGE_ORIENTATION, + orient); + if (rval < 0) + return rval; + + smiapp_update_mbus_formats(sensor); + + return 0; + + case V4L2_CID_VBLANK: + exposure = sensor->exposure->val; + + __smiapp_update_exposure_limits(sensor); + + if (exposure > sensor->exposure->maximum) { + sensor->exposure->val = + sensor->exposure->maximum; + rval = smiapp_set_ctrl( + sensor->exposure); + if (rval < 0) + return rval; + } + + return smiapp_write( + sensor, SMIAPP_REG_U16_FRAME_LENGTH_LINES, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + ctrl->val); + + case V4L2_CID_HBLANK: + return smiapp_write( + sensor, SMIAPP_REG_U16_LINE_LENGTH_PCK, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + ctrl->val); + + case V4L2_CID_LINK_FREQ: + if (sensor->streaming) + return -EBUSY; + + return smiapp_pll_update(sensor); + + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_ops smiapp_ctrl_ops = { + .s_ctrl = smiapp_set_ctrl, +}; + +static int smiapp_init_controls(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int max; + int rval; + + rval = v4l2_ctrl_handler_init(&sensor->pixel_array->ctrl_handler, 7); + if (rval) + return rval; + sensor->pixel_array->ctrl_handler.lock = &sensor->mutex; + + sensor->analog_gain = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_ANALOGUE_GAIN, + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN], + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX], + max(sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP], 1U), + sensor->limits[SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN]); + + /* Exposure limits will be updated soon, use just something here. */ + sensor->exposure = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_EXPOSURE, 0, 0, 1, 0); + + sensor->hflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HFLIP, 0, 1, 1, 0); + sensor->vflip = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VFLIP, 0, 1, 1, 0); + + sensor->vblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_VBLANK, 0, 1, 1, 0); + + if (sensor->vblank) + sensor->vblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->hblank = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_HBLANK, 0, 1, 1, 0); + + if (sensor->hblank) + sensor->hblank->flags |= V4L2_CTRL_FLAG_UPDATE; + + sensor->pixel_rate_parray = v4l2_ctrl_new_std( + &sensor->pixel_array->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->pixel_array->ctrl_handler.error) { + dev_err(&client->dev, + "pixel array controls initialization failed (%d)\n", + sensor->pixel_array->ctrl_handler.error); + rval = sensor->pixel_array->ctrl_handler.error; + goto error; + } + + sensor->pixel_array->sd.ctrl_handler = + &sensor->pixel_array->ctrl_handler; + + v4l2_ctrl_cluster(2, &sensor->hflip); + + rval = v4l2_ctrl_handler_init(&sensor->src->ctrl_handler, 0); + if (rval) + goto error; + sensor->src->ctrl_handler.lock = &sensor->mutex; + + for (max = 0; sensor->platform_data->op_sys_clock[max + 1]; max++); + + sensor->link_freq = v4l2_ctrl_new_int_menu( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_LINK_FREQ, max, 0, + sensor->platform_data->op_sys_clock); + + sensor->pixel_rate_csi = v4l2_ctrl_new_std( + &sensor->src->ctrl_handler, &smiapp_ctrl_ops, + V4L2_CID_PIXEL_RATE, 0, 0, 1, 0); + + if (sensor->src->ctrl_handler.error) { + dev_err(&client->dev, + "src controls initialization failed (%d)\n", + sensor->src->ctrl_handler.error); + rval = sensor->src->ctrl_handler.error; + goto error; + } + + sensor->src->sd.ctrl_handler = + &sensor->src->ctrl_handler; + + return 0; + +error: + v4l2_ctrl_handler_free(&sensor->pixel_array->ctrl_handler); + v4l2_ctrl_handler_free(&sensor->src->ctrl_handler); + + return rval; +} + +static void smiapp_free_controls(struct smiapp_sensor *sensor) +{ + unsigned int i; + + for (i = 0; i < sensor->ssds_used; i++) + v4l2_ctrl_handler_free(&sensor->ssds[i].ctrl_handler); +} + +static int smiapp_get_limits(struct smiapp_sensor *sensor, int const *limit, + unsigned int n) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int i; + u32 val; + int rval; + + for (i = 0; i < n; i++) { + rval = smiapp_read( + sensor, smiapp_reg_limits[limit[i]].addr, &val); + if (rval) + return rval; + sensor->limits[limit[i]] = val; + dev_dbg(&client->dev, "0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit[i]].addr, + smiapp_reg_limits[limit[i]].what, val, val); + } + + return 0; +} + +static int smiapp_get_all_limits(struct smiapp_sensor *sensor) +{ + unsigned int i; + int rval; + + for (i = 0; i < SMIAPP_LIMIT_LAST; i++) { + rval = smiapp_get_limits(sensor, &i, 1); + if (rval < 0) + return rval; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] == 0) + smiapp_replace_limit(sensor, SMIAPP_LIMIT_SCALER_N_MIN, 16); + + return 0; +} + +static int smiapp_get_limits_binning(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + static u32 const limits[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, + }; + static u32 const limits_replace[] = { + SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES, + SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN, + SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN, + }; + unsigned int i; + int rval; + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY] == + SMIAPP_BINNING_CAPABILITY_NO) { + for (i = 0; i < ARRAY_SIZE(limits); i++) + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + + return 0; + } + + rval = smiapp_get_limits(sensor, limits, ARRAY_SIZE(limits)); + if (rval < 0) + return rval; + + /* + * Sanity check whether the binning limits are valid. If not, + * use the non-binning ones. + */ + if (sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] + && sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] + && sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]) + return 0; + + for (i = 0; i < ARRAY_SIZE(limits); i++) { + dev_dbg(&client->dev, + "replace limit 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limits[i]].addr, + smiapp_reg_limits[limits[i]].what, + sensor->limits[limits_replace[i]], + sensor->limits[limits_replace[i]]); + sensor->limits[limits[i]] = + sensor->limits[limits_replace[i]]; + } + + return 0; +} + +static int smiapp_get_mbus_formats(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int type, n; + unsigned int i, pixel_order; + int rval; + + rval = smiapp_read( + sensor, SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE, &type); + if (rval) + return rval; + + dev_dbg(&client->dev, "data_format_model_type %d\n", type); + + rval = smiapp_read(sensor, SMIAPP_REG_U8_PIXEL_ORDER, + &pixel_order); + if (rval) + return rval; + + if (pixel_order >= ARRAY_SIZE(pixel_order_str)) { + dev_dbg(&client->dev, "bad pixel order %d\n", pixel_order); + return -EINVAL; + } + + dev_dbg(&client->dev, "pixel order %d (%s)\n", pixel_order, + pixel_order_str[pixel_order]); + + switch (type) { + case SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N; + break; + case SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED: + n = SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N; + break; + default: + return -EINVAL; + } + + sensor->default_pixel_order = pixel_order; + sensor->mbus_frame_fmts = 0; + + for (i = 0; i < n; i++) { + unsigned int fmt, j; + + rval = smiapp_read( + sensor, + SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(i), &fmt); + if (rval) + return rval; + + dev_dbg(&client->dev, "bpp %d, compressed %d\n", + fmt >> 8, (u8)fmt); + + for (j = 0; j < ARRAY_SIZE(smiapp_csi_data_formats); j++) { + const struct smiapp_csi_data_format *f = + &smiapp_csi_data_formats[j]; + + if (f->pixel_order != SMIAPP_PIXEL_ORDER_GRBG) + continue; + + if (f->width != fmt >> 8 || f->compressed != (u8)fmt) + continue; + + dev_dbg(&client->dev, "jolly good! %d\n", j); + + sensor->default_mbus_frame_fmts |= 1 << j; + if (!sensor->csi_format) { + sensor->csi_format = f; + sensor->internal_csi_format = f; + } + } + } + + if (!sensor->csi_format) { + dev_err(&client->dev, "no supported mbus code found\n"); + return -EINVAL; + } + + smiapp_update_mbus_formats(sensor); + + return 0; +} + +static void smiapp_update_blanking(struct smiapp_sensor *sensor) +{ + struct v4l2_ctrl *vblank = sensor->vblank; + struct v4l2_ctrl *hblank = sensor->hblank; + + vblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES], + sensor->limits[SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height); + vblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height; + + vblank->val = clamp_t(int, vblank->val, + vblank->minimum, vblank->maximum); + vblank->default_value = vblank->minimum; + vblank->val = vblank->val; + vblank->cur.val = vblank->val; + + hblank->minimum = + max_t(int, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width, + sensor->limits[SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN]); + hblank->maximum = + sensor->limits[SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN] - + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width; + + hblank->val = clamp_t(int, hblank->val, + hblank->minimum, hblank->maximum); + hblank->default_value = hblank->minimum; + hblank->val = hblank->val; + hblank->cur.val = hblank->val; + + __smiapp_update_exposure_limits(sensor); +} + +static int smiapp_update_mode(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int binning_mode; + int rval; + + dev_dbg(&client->dev, "frame size: %dx%d\n", + sensor->src->crop[SMIAPP_PAD_SRC].width, + sensor->src->crop[SMIAPP_PAD_SRC].height); + dev_dbg(&client->dev, "csi format width: %d\n", + sensor->csi_format->width); + + /* Binning has to be set up here; it affects limits */ + if (sensor->binning_horizontal == 1 && + sensor->binning_vertical == 1) { + binning_mode = 0; + } else { + u8 binning_type = + (sensor->binning_horizontal << 4) + | sensor->binning_vertical; + + rval = smiapp_write( + sensor, SMIAPP_REG_U8_BINNING_TYPE, binning_type); + if (rval < 0) + return rval; + + binning_mode = 1; + } + rval = smiapp_write(sensor, SMIAPP_REG_U8_BINNING_MODE, binning_mode); + if (rval < 0) + return rval; + + /* Get updated limits due to binning */ + rval = smiapp_get_limits_binning(sensor); + if (rval < 0) + return rval; + + rval = smiapp_pll_update(sensor); + if (rval < 0) + return rval; + + /* Output from pixel array, including blanking */ + smiapp_update_blanking(sensor); + + dev_dbg(&client->dev, "vblank\t\t%d\n", sensor->vblank->val); + dev_dbg(&client->dev, "hblank\t\t%d\n", sensor->hblank->val); + + dev_dbg(&client->dev, "real timeperframe\t100/%d\n", + sensor->pll.vt_pix_clk_freq_hz / + ((sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width + + sensor->hblank->val) * + (sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height + + sensor->vblank->val) / 100)); + + return 0; +} + +/* + * + * SMIA++ NVM handling + * + */ +static int smiapp_read_nvm(struct smiapp_sensor *sensor, + unsigned char *nvm) +{ + u32 i, s, p, np, v; + int rval = 0, rval2; + + np = sensor->nvm_size / SMIAPP_NVM_PAGE_SIZE; + for (p = 0; p < np; p++) { + rval = smiapp_write( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT, p); + if (rval) + goto out; + + rval = smiapp_write(sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, + SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN | + SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN); + if (rval) + goto out; + + for (i = 0; i < 1000; i++) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS, &s); + + if (rval) + goto out; + + if (s & SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY) + break; + + if (--i == 0) { + rval = -ETIMEDOUT; + goto out; + } + + } + + for (i = 0; i < SMIAPP_NVM_PAGE_SIZE; i++) { + rval = smiapp_read( + sensor, + SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 + i, + &v); + if (rval) + goto out; + + *nvm++ = v; + } + } + +out: + rval2 = smiapp_write(sensor, SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL, 0); + if (rval < 0) + return rval; + else + return rval2; +} + +/* + * + * SMIA++ CCI address control + * + */ +static int smiapp_change_cci_addr(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + u32 val; + + client->addr = sensor->platform_data->i2c_addr_dfl; + + rval = smiapp_write(sensor, + SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, + sensor->platform_data->i2c_addr_alt << 1); + if (rval) + return rval; + + client->addr = sensor->platform_data->i2c_addr_alt; + + /* verify addr change went ok */ + rval = smiapp_read(sensor, SMIAPP_REG_U8_CCI_ADDRESS_CONTROL, &val); + if (rval) + return rval; + + if (val != sensor->platform_data->i2c_addr_alt << 1) + return -ENODEV; + + return 0; +} + +/* + * + * SMIA++ Mode Control + * + */ +static int smiapp_setup_flash_strobe(struct smiapp_sensor *sensor) +{ + struct smiapp_flash_strobe_parms *strobe_setup; + unsigned int ext_freq = sensor->platform_data->ext_clk; + u32 tmp; + u32 strobe_adjustment; + u32 strobe_width_high_rs; + int rval; + + strobe_setup = sensor->platform_data->strobe_setup; + + /* + * How to calculate registers related to strobe length. Please + * do not change, or if you do at least know what you're + * doing. :-) + * + * Sakari Ailus 2010-10-25 + * + * flash_strobe_length [us] / 10^6 = (tFlash_strobe_width_ctrl + * / EXTCLK freq [Hz]) * flash_strobe_adjustment + * + * tFlash_strobe_width_ctrl E N, [1 - 0xffff] + * flash_strobe_adjustment E N, [1 - 0xff] + * + * The formula above is written as below to keep it on one + * line: + * + * l / 10^6 = w / e * a + * + * Let's mark w * a by x: + * + * x = w * a + * + * Thus, we get: + * + * x = l * e / 10^6 + * + * The strobe width must be at least as long as requested, + * thus rounding upwards is needed. + * + * x = (l * e + 10^6 - 1) / 10^6 + * ----------------------------- + * + * Maximum possible accuracy is wanted at all times. Thus keep + * a as small as possible. + * + * Calculate a, assuming maximum w, with rounding upwards: + * + * a = (x + (2^16 - 1) - 1) / (2^16 - 1) + * ------------------------------------- + * + * Thus, we also get w, with that a, with rounding upwards: + * + * w = (x + a - 1) / a + * ------------------- + * + * To get limits: + * + * x E [1, (2^16 - 1) * (2^8 - 1)] + * + * Substituting maximum x to the original formula (with rounding), + * the maximum l is thus + * + * (2^16 - 1) * (2^8 - 1) * 10^6 = l * e + 10^6 - 1 + * + * l = (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / e + * -------------------------------------------------- + * + * flash_strobe_length must be clamped between 1 and + * (10^6 * (2^16 - 1) * (2^8 - 1) - 10^6 + 1) / EXTCLK freq. + * + * Then, + * + * flash_strobe_adjustment = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + (2^16 - 1) - 1) / (2^16 - 1) + * + * tFlash_strobe_width_ctrl = ((flash_strobe_length * + * EXTCLK freq + 10^6 - 1) / 10^6 + + * flash_strobe_adjustment - 1) / flash_strobe_adjustment + */ + tmp = div_u64(1000000ULL * ((1 << 16) - 1) * ((1 << 8) - 1) - + 1000000 + 1, ext_freq); + strobe_setup->strobe_width_high_us = + clamp_t(u32, strobe_setup->strobe_width_high_us, 1, tmp); + + tmp = div_u64(((u64)strobe_setup->strobe_width_high_us * (u64)ext_freq + + 1000000 - 1), 1000000ULL); + strobe_adjustment = (tmp + (1 << 16) - 1 - 1) / ((1 << 16) - 1); + strobe_width_high_rs = (tmp + strobe_adjustment - 1) / + strobe_adjustment; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_MODE_RS, + strobe_setup->mode); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT, + strobe_adjustment); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL, + strobe_width_high_rs); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL, + strobe_setup->strobe_delay); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_FLASH_STROBE_START_POINT, + strobe_setup->stobe_start_point); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FLASH_TRIGGER_RS, + strobe_setup->trigger); + +out: + sensor->platform_data->strobe_setup->trigger = 0; + + return rval; +} + +/* ----------------------------------------------------------------------------- + * Power management + */ + +static int smiapp_power_on(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int sleep; + int rval; + + rval = regulator_enable(sensor->vana); + if (rval) { + dev_err(&client->dev, "failed to enable vana regulator\n"); + return rval; + } + usleep_range(1000, 1000); + + if (sensor->platform_data->set_xclk) + rval = sensor->platform_data->set_xclk( + &sensor->src->sd, sensor->platform_data->ext_clk); + else + rval = clk_enable(sensor->ext_clk); + if (rval < 0) { + dev_dbg(&client->dev, "failed to set xclk\n"); + goto out_xclk_fail; + } + usleep_range(1000, 1000); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 1); + + sleep = SMIAPP_RESET_DELAY(sensor->platform_data->ext_clk); + usleep_range(sleep, sleep); + + /* + * Failures to respond to the address change command have been noticed. + * Those failures seem to be caused by the sensor requiring a longer + * boot time than advertised. An additional 10ms delay seems to work + * around the issue, but the SMIA++ I2C write retry hack makes the delay + * unnecessary. The failures need to be investigated to find a proper + * fix, and a delay will likely need to be added here if the I2C write + * retry hack is reverted before the root cause of the boot time issue + * is found. + */ + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + if (rval < 0) { + dev_err(&client->dev, "software reset failed\n"); + goto out_cci_addr_fail; + } + + if (sensor->platform_data->i2c_addr_alt) { + rval = smiapp_change_cci_addr(sensor); + if (rval) { + dev_err(&client->dev, "cci address change error\n"); + goto out_cci_addr_fail; + } + } + + rval = smiapp_write(sensor, SMIAPP_REG_U16_COMPRESSION_MODE, + SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR); + if (rval) { + dev_err(&client->dev, "compression mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ, + sensor->platform_data->ext_clk / (1000000 / (1 << 8))); + if (rval) { + dev_err(&client->dev, "extclk frequency set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_LANE_MODE, + sensor->platform_data->lanes - 1); + if (rval) { + dev_err(&client->dev, "csi lane mode set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_FAST_STANDBY_CTRL, + SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE); + if (rval) { + dev_err(&client->dev, "fast standby set failed\n"); + goto out_cci_addr_fail; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_CSI_SIGNALLING_MODE, + sensor->platform_data->csi_signalling_mode); + if (rval) { + dev_err(&client->dev, "csi signalling mode set failed\n"); + goto out_cci_addr_fail; + } + + /* DPHY control done by sensor based on requested link rate */ + rval = smiapp_write(sensor, SMIAPP_REG_U8_DPHY_CTRL, + SMIAPP_DPHY_CTRL_UI); + if (rval < 0) + return rval; + + rval = smiapp_call_quirk(sensor, post_poweron); + if (rval) { + dev_err(&client->dev, "post_poweron quirks failed\n"); + goto out_cci_addr_fail; + } + + /* Are we still initialising...? If yes, return here. */ + if (!sensor->pixel_array) + return 0; + + rval = v4l2_ctrl_handler_setup( + &sensor->pixel_array->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + rval = v4l2_ctrl_handler_setup(&sensor->src->ctrl_handler); + if (rval) + goto out_cci_addr_fail; + + mutex_lock(&sensor->mutex); + rval = smiapp_update_mode(sensor); + mutex_unlock(&sensor->mutex); + if (rval < 0) + goto out_cci_addr_fail; + + return 0; + +out_cci_addr_fail: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + +out_xclk_fail: + regulator_disable(sensor->vana); + return rval; +} + +static void smiapp_power_off(struct smiapp_sensor *sensor) +{ + /* + * Currently power/clock to lens are enable/disabled separately + * but they are essentially the same signals. So if the sensor is + * powered off while the lens is powered on the sensor does not + * really see a power off and next time the cci address change + * will fail. So do a soft reset explicitly here. + */ + if (sensor->platform_data->i2c_addr_alt) + smiapp_write(sensor, + SMIAPP_REG_U8_SOFTWARE_RESET, + SMIAPP_SOFTWARE_RESET); + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + usleep_range(5000, 5000); + regulator_disable(sensor->vana); + sensor->streaming = 0; +} + +static int smiapp_set_power(struct v4l2_subdev *subdev, int on) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret = 0; + + mutex_lock(&sensor->power_mutex); + + /* + * If the power count is modified from 0 to != 0 or from != 0 + * to 0, update the power state. + */ + if (!sensor->power_count == !on) + goto out; + + if (on) { + /* Power on and perform initialisation. */ + ret = smiapp_power_on(sensor); + if (ret < 0) + goto out; + } else { + smiapp_power_off(sensor); + } + + /* Update the power count. */ + sensor->power_count += on ? 1 : -1; + WARN_ON(sensor->power_count < 0); + +out: + mutex_unlock(&sensor->power_mutex); + return ret; +} + +/* ----------------------------------------------------------------------------- + * Video stream management + */ + +static int smiapp_start_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + + rval = smiapp_write(sensor, SMIAPP_REG_U16_CSI_DATA_FORMAT, + (sensor->csi_format->width << 8) | + sensor->csi_format->compressed); + if (rval) + goto out; + + rval = smiapp_pll_configure(sensor); + if (rval) + goto out; + + /* Analog crop start coordinates */ + rval = smiapp_write(sensor, SMIAPP_REG_U16_X_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_ADDR_START, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top); + if (rval < 0) + goto out; + + /* Analog crop end coordinates */ + rval = smiapp_write( + sensor, SMIAPP_REG_U16_X_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].left + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].width - 1); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_Y_ADDR_END, + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].top + + sensor->pixel_array->crop[SMIAPP_PA_PAD_SRC].height - 1); + if (rval < 0) + goto out; + + /* + * Output from pixel array, including blanking, is set using + * controls below. No need to set here. + */ + + /* Digital crop */ + if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].left); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET, + sensor->scaler->crop[SMIAPP_PAD_SINK].top); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH, + sensor->scaler->crop[SMIAPP_PAD_SINK].width); + if (rval < 0) + goto out; + + rval = smiapp_write( + sensor, SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT, + sensor->scaler->crop[SMIAPP_PAD_SINK].height); + if (rval < 0) + goto out; + } + + /* Scaling */ + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALING_MODE, + sensor->scaling_mode); + if (rval < 0) + goto out; + + rval = smiapp_write(sensor, SMIAPP_REG_U16_SCALE_M, + sensor->scale_m); + if (rval < 0) + goto out; + } + + /* Output size from sensor */ + rval = smiapp_write(sensor, SMIAPP_REG_U16_X_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].width); + if (rval < 0) + goto out; + rval = smiapp_write(sensor, SMIAPP_REG_U16_Y_OUTPUT_SIZE, + sensor->src->crop[SMIAPP_PAD_SRC].height); + if (rval < 0) + goto out; + + if ((sensor->flash_capability & + (SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE | + SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE)) && + sensor->platform_data->strobe_setup != NULL && + sensor->platform_data->strobe_setup->trigger != 0) { + rval = smiapp_setup_flash_strobe(sensor); + if (rval) + goto out; + } + + rval = smiapp_call_quirk(sensor, pre_streamon); + if (rval) { + dev_err(&client->dev, "pre_streamon quirks failed\n"); + goto out; + } + + rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_STREAMING); + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static int smiapp_stop_streaming(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + mutex_lock(&sensor->mutex); + rval = smiapp_write(sensor, SMIAPP_REG_U8_MODE_SELECT, + SMIAPP_MODE_SELECT_SOFTWARE_STANDBY); + if (rval) + goto out; + + rval = smiapp_call_quirk(sensor, post_streamoff); + if (rval) + dev_err(&client->dev, "post_streamoff quirks failed\n"); + +out: + mutex_unlock(&sensor->mutex); + return rval; +} + +/* ----------------------------------------------------------------------------- + * V4L2 subdev video operations + */ + +static int smiapp_set_stream(struct v4l2_subdev *subdev, int enable) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->streaming == enable) + return 0; + + if (enable) { + sensor->streaming = 1; + rval = smiapp_start_streaming(sensor); + if (rval < 0) + sensor->streaming = 0; + } else { + rval = smiapp_stop_streaming(sensor); + sensor->streaming = 0; + } + + return rval; +} + +static int smiapp_enum_mbus_code(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_mbus_code_enum *code) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + int idx = -1; + int rval = -EINVAL; + + mutex_lock(&sensor->mutex); + + dev_err(&client->dev, "subdev %s, pad %d, index %d\n", + subdev->name, code->pad, code->index); + + if (subdev != &sensor->src->sd || code->pad != SMIAPP_PAD_SRC) { + if (code->index) + goto out; + + code->code = sensor->internal_csi_format->code; + rval = 0; + goto out; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i)) + idx++; + + if (idx == code->index) { + code->code = smiapp_csi_data_formats[i].code; + dev_err(&client->dev, "found index %d, i %d, code %x\n", + code->index, i, code->code); + rval = 0; + break; + } + } + +out: + mutex_unlock(&sensor->mutex); + + return rval; +} + +static u32 __smiapp_get_mbus_code(struct v4l2_subdev *subdev, + unsigned int pad) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + if (subdev == &sensor->src->sd && pad == SMIAPP_PAD_SRC) + return sensor->csi_format->code; + else + return sensor->internal_csi_format->code; +} + +static int __smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + if (fmt->which == V4L2_SUBDEV_FORMAT_TRY) { + fmt->format = *v4l2_subdev_get_try_format(fh, fmt->pad); + } else { + struct v4l2_rect *r; + + if (fmt->pad == ssd->source_pad) + r = &ssd->crop[ssd->source_pad]; + else + r = &ssd->sink_fmt; + + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width = r->width; + fmt->format.height = r->height; + } + + return 0; +} + +static int smiapp_get_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_format(subdev, fh, fmt); + mutex_unlock(&sensor->mutex); + + return rval; +} + +static void smiapp_get_crop_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_rect **crops, + struct v4l2_rect **comps, int which) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + unsigned int i; + + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (crops) + for (i = 0; i < subdev->entity.num_pads; i++) + crops[i] = &ssd->crop[i]; + if (comps) + *comps = &ssd->compose; + } else { + if (crops) { + for (i = 0; i < subdev->entity.num_pads; i++) { + crops[i] = v4l2_subdev_get_try_crop(fh, i); + BUG_ON(!crops[i]); + } + } + if (comps) { + *comps = v4l2_subdev_get_try_compose(fh, + SMIAPP_PAD_SINK); + BUG_ON(!*comps); + } + } +} + +/* Changes require propagation only on sink pad. */ +static void smiapp_propagate(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, int which, + int target) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, which); + + switch (target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + comp->width = crops[SMIAPP_PAD_SINK]->width; + comp->height = crops[SMIAPP_PAD_SINK]->height; + if (which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (ssd == sensor->scaler) { + sensor->scale_m = + sensor->limits[ + SMIAPP_LIMIT_SCALER_N_MIN]; + sensor->scaling_mode = + SMIAPP_SCALING_MODE_NONE; + } else if (ssd == sensor->binner) { + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + } + } + /* Fall through */ + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + *crops[SMIAPP_PAD_SRC] = *comp; + break; + default: + BUG(); + } +} + +static const struct smiapp_csi_data_format +*smiapp_validate_csi_data_format(struct smiapp_sensor *sensor, u32 code) +{ + const struct smiapp_csi_data_format *csi_format = sensor->csi_format; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(smiapp_csi_data_formats); i++) { + if (sensor->mbus_frame_fmts & (1 << i) + && smiapp_csi_data_formats[i].code == code) + return &smiapp_csi_data_formats[i]; + } + + return csi_format; +} + +static int smiapp_set_format(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_format *fmt) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *crops[SMIAPP_PADS]; + + mutex_lock(&sensor->mutex); + + /* + * Media bus code is changeable on src subdev's source pad. On + * other source pads we just get format here. + */ + if (fmt->pad == ssd->source_pad) { + u32 code = fmt->format.code; + int rval = __smiapp_get_format(subdev, fh, fmt); + + if (!rval && subdev == &sensor->src->sd) { + const struct smiapp_csi_data_format *csi_format = + smiapp_validate_csi_data_format(sensor, code); + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + sensor->csi_format = csi_format; + fmt->format.code = csi_format->code; + } + + mutex_unlock(&sensor->mutex); + return rval; + } + + /* Sink pad. Width and height are changeable here. */ + fmt->format.code = __smiapp_get_mbus_code(subdev, fmt->pad); + fmt->format.width &= ~1; + fmt->format.height &= ~1; + + fmt->format.width = + clamp(fmt->format.width, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE]); + fmt->format.height = + clamp(fmt->format.height, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sensor->limits[SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE]); + + smiapp_get_crop_compose(subdev, fh, crops, NULL, fmt->which); + + crops[ssd->sink_pad]->left = 0; + crops[ssd->sink_pad]->top = 0; + crops[ssd->sink_pad]->width = fmt->format.width; + crops[ssd->sink_pad]->height = fmt->format.height; + if (fmt->which == V4L2_SUBDEV_FORMAT_ACTIVE) + ssd->sink_fmt = *crops[ssd->sink_pad]; + smiapp_propagate(subdev, fh, fmt->which, + V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + + mutex_unlock(&sensor->mutex); + + return 0; +} + +/* + * Calculate goodness of scaled image size compared to expected image + * size and flags provided. + */ +#define SCALING_GOODNESS 100000 +#define SCALING_GOODNESS_EXTREME 100000000 +static int scaling_goodness(struct v4l2_subdev *subdev, int w, int ask_w, + int h, int ask_h, u32 flags) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + int val = 0; + + w &= ~1; + ask_w &= ~1; + h &= ~1; + ask_h &= ~1; + + if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_GE) { + if (w < ask_w) + val -= SCALING_GOODNESS; + if (h < ask_h) + val -= SCALING_GOODNESS; + } + + if (flags & V4L2_SUBDEV_SEL_FLAG_SIZE_LE) { + if (w > ask_w) + val -= SCALING_GOODNESS; + if (h > ask_h) + val -= SCALING_GOODNESS; + } + + val -= abs(w - ask_w); + val -= abs(h - ask_h); + + if (w < sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]) + val -= SCALING_GOODNESS_EXTREME; + + dev_dbg(&client->dev, "w %d ask_w %d h %d ask_h %d goodness %d\n", + w, ask_h, h, ask_h, val); + + return val; +} + +static void smiapp_set_compose_binner(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + unsigned int binh = 1, binv = 1; + unsigned int best = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width, sel->r.width, + crops[SMIAPP_PAD_SINK]->height, sel->r.height, sel->flags); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / sensor->binning_subtypes[i].horizontal, + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / sensor->binning_subtypes[i].vertical, + sel->r.height, sel->flags); + + if (this > best) { + binh = sensor->binning_subtypes[i].horizontal; + binv = sensor->binning_subtypes[i].vertical; + best = this; + } + } + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->binning_vertical = binv; + sensor->binning_horizontal = binh; + } + + sel->r.width = (crops[SMIAPP_PAD_SINK]->width / binh) & ~1; + sel->r.height = (crops[SMIAPP_PAD_SINK]->height / binv) & ~1; +} + +/* + * Calculate best scaling ratio and mode for given output resolution. + * + * Try all of these: horizontal ratio, vertical ratio and smallest + * size possible (horizontally). + * + * Also try whether horizontal scaler or full scaler gives a better + * result. + */ +static void smiapp_set_compose_scaler(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel, + struct v4l2_rect **crops, + struct v4l2_rect *comp) +{ + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + u32 min, max, a, b, max_m; + u32 scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + int mode = SMIAPP_SCALING_MODE_HORIZONTAL; + u32 try[4]; + u32 ntry = 0; + unsigned int i; + int best = INT_MIN; + + sel->r.width = min_t(unsigned int, sel->r.width, + crops[SMIAPP_PAD_SINK]->width); + sel->r.height = min_t(unsigned int, sel->r.height, + crops[SMIAPP_PAD_SINK]->height); + + a = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.width; + b = crops[SMIAPP_PAD_SINK]->height + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] / sel->r.height; + max_m = crops[SMIAPP_PAD_SINK]->width + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN] + / sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE]; + + a = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(a, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + b = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(b, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + max_m = min(sensor->limits[SMIAPP_LIMIT_SCALER_M_MAX], + max(max_m, sensor->limits[SMIAPP_LIMIT_SCALER_M_MIN])); + + dev_dbg(&client->dev, "scaling: a %d b %d max_m %d\n", a, b, max_m); + + min = min(max_m, min(a, b)); + max = min(max_m, max(a, b)); + + try[ntry] = min; + ntry++; + if (min != max) { + try[ntry] = max; + ntry++; + } + if (max != max_m) { + try[ntry] = min + 1; + ntry++; + if (min != max) { + try[ntry] = max + 1; + ntry++; + } + } + + for (i = 0; i < ntry; i++) { + int this = scaling_goodness( + subdev, + crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height, + sel->r.height, + sel->flags); + + dev_dbg(&client->dev, "trying factor %d (%d)\n", try[i], i); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_HORIZONTAL; + best = this; + } + + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + continue; + + this = scaling_goodness( + subdev, crops[SMIAPP_PAD_SINK]->width + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.width, + crops[SMIAPP_PAD_SINK]->height + / try[i] + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN], + sel->r.height, + sel->flags); + + if (this > best) { + scale_m = try[i]; + mode = SMIAPP_SCALING_MODE_BOTH; + best = this; + } + } + + sel->r.width = + (crops[SMIAPP_PAD_SINK]->width + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) & ~1; + if (mode == SMIAPP_SCALING_MODE_BOTH) + sel->r.height = + (crops[SMIAPP_PAD_SINK]->height + / scale_m + * sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]) + & ~1; + else + sel->r.height = crops[SMIAPP_PAD_SINK]->height; + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sensor->scale_m = scale_m; + sensor->scaling_mode = mode; + } +} +/* We're only called on source pads. This function sets scaling. */ +static int smiapp_set_compose(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + sel->r.top = 0; + sel->r.left = 0; + + if (ssd == sensor->binner) + smiapp_set_compose_binner(subdev, fh, sel, crops, comp); + else + smiapp_set_compose_scaler(subdev, fh, sel, crops, comp); + + *comp = sel->r; + smiapp_propagate(subdev, fh, sel->which, + V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) + return smiapp_update_mode(sensor); + + return 0; +} + +static int __smiapp_sel_supported(struct v4l2_subdev *subdev, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + + /* We only implement crop in three places. */ + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array + && sel->pad == SMIAPP_PA_PAD_SRC) + return 0; + if (ssd == sensor->src + && sel->pad == SMIAPP_PAD_SRC) + return 0; + if (ssd == sensor->scaler + && sel->pad == SMIAPP_PAD_SINK + && sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) + return 0; + return -EINVAL; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + if (sel->pad == ssd->source_pad) + return -EINVAL; + if (ssd == sensor->binner) + return 0; + if (ssd == sensor->scaler + && sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) + return 0; + /* Fall through */ + default: + return -EINVAL; + } +} + +static int smiapp_set_crop(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *src_size, *crops[SMIAPP_PADS]; + struct v4l2_rect _r; + + smiapp_get_crop_compose(subdev, fh, crops, NULL, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + if (sel->pad == ssd->sink_pad) + src_size = &ssd->sink_fmt; + else + src_size = &ssd->compose; + } else { + if (sel->pad == ssd->sink_pad) { + _r.left = 0; + _r.top = 0; + _r.width = v4l2_subdev_get_try_format(fh, sel->pad) + ->width; + _r.height = v4l2_subdev_get_try_format(fh, sel->pad) + ->height; + src_size = &_r; + } else { + src_size = + v4l2_subdev_get_try_compose( + fh, ssd->sink_pad); + } + } + + if (ssd == sensor->src && sel->pad == SMIAPP_PAD_SRC) { + sel->r.left = 0; + sel->r.top = 0; + } + + sel->r.width = min(sel->r.width, src_size->width); + sel->r.height = min(sel->r.height, src_size->height); + + sel->r.left = min(sel->r.left, src_size->width - sel->r.width); + sel->r.top = min(sel->r.top, src_size->height - sel->r.height); + + *crops[sel->pad] = sel->r; + + if (ssd != sensor->pixel_array && sel->pad == SMIAPP_PAD_SINK) + smiapp_propagate(subdev, fh, sel->which, + V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL); + + return 0; +} + +static int __smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct smiapp_subdev *ssd = to_smiapp_subdev(subdev); + struct v4l2_rect *comp, *crops[SMIAPP_PADS]; + struct v4l2_rect sink_fmt; + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + smiapp_get_crop_compose(subdev, fh, crops, &comp, sel->which); + + if (sel->which == V4L2_SUBDEV_FORMAT_ACTIVE) { + sink_fmt = ssd->sink_fmt; + } else { + struct v4l2_mbus_framefmt *fmt = + v4l2_subdev_get_try_format(fh, ssd->sink_pad); + + sink_fmt.left = 0; + sink_fmt.top = 0; + sink_fmt.width = fmt->width; + sink_fmt.height = fmt->height; + } + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS: + if (ssd == sensor->pixel_array) { + sel->r.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + sel->r.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + } else if (sel->pad == ssd->sink_pad) { + sel->r = sink_fmt; + } else { + sel->r = *comp; + } + break; + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + case V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS: + sel->r = *crops[sel->pad]; + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + sel->r = *comp; + break; + } + + return 0; +} + +static int smiapp_get_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + mutex_lock(&sensor->mutex); + rval = __smiapp_get_selection(subdev, fh, sel); + mutex_unlock(&sensor->mutex); + + return rval; +} +static int smiapp_set_selection(struct v4l2_subdev *subdev, + struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int ret; + + ret = __smiapp_sel_supported(subdev, sel); + if (ret) + return ret; + + mutex_lock(&sensor->mutex); + + sel->r.left = max(0, sel->r.left & ~1); + sel->r.top = max(0, sel->r.top & ~1); + sel->r.width = max(0, SMIAPP_ALIGN_DIM(sel->r.width, sel->flags)); + sel->r.height = max(0, SMIAPP_ALIGN_DIM(sel->r.height, sel->flags)); + + sel->r.width = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE], + sel->r.width); + sel->r.height = max_t(unsigned int, + sensor->limits[SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE], + sel->r.height); + + switch (sel->target) { + case V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL: + ret = smiapp_set_crop(subdev, fh, sel); + break; + case V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL: + ret = smiapp_set_compose(subdev, fh, sel); + break; + default: + BUG(); + } + + mutex_unlock(&sensor->mutex); + return ret; +} + +static int smiapp_get_skip_frames(struct v4l2_subdev *subdev, u32 *frames) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + + *frames = sensor->frame_skip; + return 0; +} + +/* ----------------------------------------------------------------------------- + * sysfs attributes + */ + +static ssize_t +smiapp_sysfs_nvm_read(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int nbytes; + + if (!sensor->dev_init_done) + return -EBUSY; + + if (!sensor->nvm_size) { + /* NVM not read yet - read it now */ + sensor->nvm_size = sensor->platform_data->nvm_size; + if (smiapp_set_power(subdev, 1) < 0) + return -ENODEV; + if (smiapp_read_nvm(sensor, sensor->nvm)) { + dev_err(&client->dev, "nvm read failed\n"); + return -ENODEV; + } + smiapp_set_power(subdev, 0); + } + /* + * NVM is still way below a PAGE_SIZE, so we can safely + * assume this for now. + */ + nbytes = min_t(unsigned int, sensor->nvm_size, PAGE_SIZE); + memcpy(buf, sensor->nvm, nbytes); + + return nbytes; +} +static DEVICE_ATTR(nvm, S_IRUGO, smiapp_sysfs_nvm_read, NULL); + +/* ----------------------------------------------------------------------------- + * V4L2 subdev core operations + */ + +static int smiapp_identify_module(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_module_info *minfo = &sensor->minfo; + unsigned int i; + int rval = 0; + + minfo->name = SMIAPP_NAME; + + /* Module info */ + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MANUFACTURER_ID, + &minfo->manufacturer_id); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U16_MODEL_ID, + &minfo->model_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_REVISION_NUMBER_MAJOR, + &minfo->revision_number_major); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_REVISION_NUMBER_MINOR, + &minfo->revision_number_minor); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_MODULE_DATE_YEAR, + &minfo->module_year); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_MODULE_DATE_MONTH, + &minfo->module_month); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_MODULE_DATE_DAY, + &minfo->module_day); + + /* Sensor info */ + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID, + &minfo->sensor_manufacturer_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U16_SENSOR_MODEL_ID, + &minfo->sensor_model_id); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_REVISION_NUMBER, + &minfo->sensor_revision_number); + if (!rval) + rval = smiapp_read_8only(sensor, + SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION, + &minfo->sensor_firmware_version); + + /* SMIA */ + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIA_VERSION, + &minfo->smia_version); + if (!rval) + rval = smiapp_read_8only(sensor, SMIAPP_REG_U8_SMIAPP_VERSION, + &minfo->smiapp_version); + + if (rval) { + dev_err(&client->dev, "sensor detection failed\n"); + return -ENODEV; + } + + dev_dbg(&client->dev, "module 0x%2.2x-0x%4.4x\n", + minfo->manufacturer_id, minfo->model_id); + + dev_dbg(&client->dev, + "module revision 0x%2.2x-0x%2.2x date %2.2d-%2.2d-%2.2d\n", + minfo->revision_number_major, minfo->revision_number_minor, + minfo->module_year, minfo->module_month, minfo->module_day); + + dev_dbg(&client->dev, "sensor 0x%2.2x-0x%4.4x\n", + minfo->sensor_manufacturer_id, minfo->sensor_model_id); + + dev_dbg(&client->dev, + "sensor revision 0x%2.2x firmware version 0x%2.2x\n", + minfo->sensor_revision_number, minfo->sensor_firmware_version); + + dev_dbg(&client->dev, "smia version %2.2d smiapp version %2.2d\n", + minfo->smia_version, minfo->smiapp_version); + + /* + * Some modules have bad data in the lvalues below. Hope the + * rvalues have better stuff. The lvalues are module + * parameters whereas the rvalues are sensor parameters. + */ + if (!minfo->manufacturer_id && !minfo->model_id) { + minfo->manufacturer_id = minfo->sensor_manufacturer_id; + minfo->model_id = minfo->sensor_model_id; + minfo->revision_number_major = minfo->sensor_revision_number; + } + + for (i = 0; i < ARRAY_SIZE(smiapp_module_idents); i++) { + if (smiapp_module_idents[i].manufacturer_id + != minfo->manufacturer_id) + continue; + if (smiapp_module_idents[i].model_id != minfo->model_id) + continue; + if (smiapp_module_idents[i].flags + & SMIAPP_MODULE_IDENT_FLAG_REV_LE) { + if (smiapp_module_idents[i].revision_number_major + < minfo->revision_number_major) + continue; + } else { + if (smiapp_module_idents[i].revision_number_major + != minfo->revision_number_major) + continue; + } + + minfo->name = smiapp_module_idents[i].name; + minfo->quirk = smiapp_module_idents[i].quirk; + break; + } + + if (i >= ARRAY_SIZE(smiapp_module_idents)) + dev_warn(&client->dev, + "no quirks for this module; let's hope it's fully compliant\n"); + + dev_dbg(&client->dev, "the sensor is called %s, ident %2.2x%4.4x%2.2x\n", + minfo->name, minfo->manufacturer_id, minfo->model_id, + minfo->revision_number_major); + + strlcpy(subdev->name, sensor->minfo.name, sizeof(subdev->name)); + + return 0; +} + +static const struct v4l2_subdev_ops smiapp_ops; +static const struct v4l2_subdev_internal_ops smiapp_internal_ops; +static const struct media_entity_operations smiapp_entity_ops; + +static int smiapp_registered(struct v4l2_subdev *subdev) +{ + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + struct i2c_client *client = v4l2_get_subdevdata(subdev); + struct smiapp_subdev *last = NULL; + u32 tmp; + unsigned int i; + int rval; + + sensor->vana = regulator_get(&client->dev, "VANA"); + if (IS_ERR(sensor->vana)) { + dev_err(&client->dev, "could not get regulator for vana\n"); + return -ENODEV; + } + + if (!sensor->platform_data->set_xclk) { + sensor->ext_clk = clk_get(&client->dev, + sensor->platform_data->ext_clk_name); + if (IS_ERR(sensor->ext_clk)) { + dev_err(&client->dev, "could not get clock %s\n", + sensor->platform_data->ext_clk_name); + rval = -ENODEV; + goto out_clk_get; + } + + rval = clk_set_rate(sensor->ext_clk, + sensor->platform_data->ext_clk); + if (rval < 0) { + dev_err(&client->dev, + "unable to set clock %s freq to %u\n", + sensor->platform_data->ext_clk_name, + sensor->platform_data->ext_clk); + rval = -ENODEV; + goto out_clk_set_rate; + } + } + + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) { + if (gpio_request_one(sensor->platform_data->xshutdown, 0, + "SMIA++ xshutdown") != 0) { + dev_err(&client->dev, + "unable to acquire reset gpio %d\n", + sensor->platform_data->xshutdown); + rval = -ENODEV; + goto out_clk_set_rate; + } + } + + rval = smiapp_power_on(sensor); + if (rval) { + rval = -ENODEV; + goto out_smiapp_power_on; + } + + rval = smiapp_identify_module(subdev); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + rval = smiapp_get_all_limits(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + /* + * Handle Sensor Module orientation on the board. + * + * The application of H-FLIP and V-FLIP on the sensor is modified by + * the sensor orientation on the board. + * + * For SMIAPP_BOARD_SENSOR_ORIENT_180 the default behaviour is to set + * both H-FLIP and V-FLIP for normal operation which also implies + * that a set/unset operation for user space HFLIP and VFLIP v4l2 + * controls will need to be internally inverted. + * + * Rotation also changes the bayer pattern. + */ + if (sensor->platform_data->module_board_orient == + SMIAPP_MODULE_BOARD_ORIENT_180) + sensor->hvflip_inv_mask = SMIAPP_IMAGE_ORIENTATION_HFLIP | + SMIAPP_IMAGE_ORIENTATION_VFLIP; + + rval = smiapp_get_mbus_formats(sensor); + if (rval) { + rval = -ENODEV; + goto out_power_off; + } + + if (sensor->limits[SMIAPP_LIMIT_BINNING_CAPABILITY]) { + u32 val; + + rval = smiapp_read(sensor, + SMIAPP_REG_U8_BINNING_SUBTYPES, &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->nbinning_subtypes = min_t(u8, val, + SMIAPP_BINNING_SUBTYPES); + + for (i = 0; i < sensor->nbinning_subtypes; i++) { + rval = smiapp_read( + sensor, SMIAPP_REG_U8_BINNING_TYPE_n(i), &val); + if (rval < 0) { + rval = -ENODEV; + goto out_power_off; + } + sensor->binning_subtypes[i] = + *(struct smiapp_binning_subtype *)&val; + + dev_dbg(&client->dev, "binning %xx%x\n", + sensor->binning_subtypes[i].horizontal, + sensor->binning_subtypes[i].vertical); + } + } + sensor->binning_horizontal = 1; + sensor->binning_vertical = 1; + + /* SMIA++ NVM initialization - it will be read from the sensor + * when it is first requested by userspace. + */ + if (sensor->minfo.smiapp_version && sensor->platform_data->nvm_size) { + sensor->nvm = kzalloc(sensor->platform_data->nvm_size, + GFP_KERNEL); + if (sensor->nvm == NULL) { + dev_err(&client->dev, "nvm buf allocation failed\n"); + rval = -ENOMEM; + goto out_power_off; + } + + if (device_create_file(&client->dev, &dev_attr_nvm) != 0) { + dev_err(&client->dev, "sysfs nvm entry failed\n"); + rval = -EBUSY; + goto out_power_off; + } + } + + rval = smiapp_call_quirk(sensor, limits); + if (rval) { + dev_err(&client->dev, "limits quirks failed\n"); + goto out_nvm_release; + } + + /* We consider this as profile 0 sensor if any of these are zero. */ + if (!sensor->limits[SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV] || + !sensor->limits[SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV]) { + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_0; + } else if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + != SMIAPP_SCALING_CAPABILITY_NONE) { + if (sensor->limits[SMIAPP_LIMIT_SCALING_CAPABILITY] + == SMIAPP_SCALING_CAPABILITY_HORIZONTAL) + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_1; + else + sensor->minfo.smiapp_profile = SMIAPP_PROFILE_2; + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } else if (sensor->limits[SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY] + == SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP) { + sensor->scaler = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + } + sensor->binner = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + sensor->pixel_array = &sensor->ssds[sensor->ssds_used]; + sensor->ssds_used++; + + sensor->scale_m = sensor->limits[SMIAPP_LIMIT_SCALER_N_MIN]; + + for (i = 0; i < SMIAPP_SUBDEVS; i++) { + struct { + struct smiapp_subdev *ssd; + char *name; + } const __this[] = { + { sensor->scaler, "scaler", }, + { sensor->binner, "binner", }, + { sensor->pixel_array, "pixel array", }, + }, *_this = &__this[i]; + struct smiapp_subdev *this = _this->ssd; + + if (!this) + continue; + + if (this != sensor->src) + v4l2_subdev_init(&this->sd, &smiapp_ops); + + this->sensor = sensor; + + if (this == sensor->pixel_array) { + this->npads = 1; + } else { + this->npads = 2; + this->source_pad = 1; + } + + snprintf(this->sd.name, + sizeof(this->sd.name), "%s %s", + sensor->minfo.name, _this->name); + + this->sink_fmt.width = + sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + this->sink_fmt.height = + sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + this->compose.width = this->sink_fmt.width; + this->compose.height = this->sink_fmt.height; + this->crop[this->source_pad] = this->compose; + this->pads[this->source_pad].flags = MEDIA_PAD_FL_SOURCE; + if (this != sensor->pixel_array) { + this->crop[this->sink_pad] = this->compose; + this->pads[this->sink_pad].flags = MEDIA_PAD_FL_SINK; + } + + this->sd.entity.ops = &smiapp_entity_ops; + + if (last == NULL) { + last = this; + continue; + } + + this->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + this->sd.internal_ops = &smiapp_internal_ops; + this->sd.owner = NULL; + v4l2_set_subdevdata(&this->sd, client); + + rval = media_entity_init(&this->sd.entity, + this->npads, this->pads, 0); + if (rval) { + dev_err(&client->dev, + "media_entity_init failed\n"); + goto out_nvm_release; + } + + rval = media_entity_create_link(&this->sd.entity, + this->source_pad, + &last->sd.entity, + last->sink_pad, + MEDIA_LNK_FL_ENABLED | + MEDIA_LNK_FL_IMMUTABLE); + if (rval) { + dev_err(&client->dev, + "media_entity_create_link failed\n"); + goto out_nvm_release; + } + + rval = v4l2_device_register_subdev(sensor->src->sd.v4l2_dev, + &this->sd); + if (rval) { + dev_err(&client->dev, + "v4l2_device_register_subdev failed\n"); + goto out_nvm_release; + } + + last = this; + } + + dev_dbg(&client->dev, "profile %d\n", sensor->minfo.smiapp_profile); + + sensor->pixel_array->sd.entity.type = MEDIA_ENT_T_V4L2_SUBDEV_SENSOR; + + /* final steps */ + smiapp_read_frame_fmt(sensor); + rval = smiapp_init_controls(sensor); + if (rval < 0) + goto out_nvm_release; + + rval = smiapp_update_mode(sensor); + if (rval) { + dev_err(&client->dev, "update mode failed\n"); + goto out_nvm_release; + } + + sensor->streaming = false; + sensor->dev_init_done = true; + + /* check flash capability */ + rval = smiapp_read(sensor, SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, &tmp); + sensor->flash_capability = tmp; + if (rval) + goto out_nvm_release; + + smiapp_power_off(sensor); + + return 0; + +out_nvm_release: + device_remove_file(&client->dev, &dev_attr_nvm); + +out_power_off: + kfree(sensor->nvm); + sensor->nvm = NULL; + smiapp_power_off(sensor); + +out_smiapp_power_on: + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + +out_clk_set_rate: + clk_put(sensor->ext_clk); + sensor->ext_clk = NULL; + +out_clk_get: + regulator_put(sensor->vana); + sensor->vana = NULL; + return rval; +} + +static int smiapp_open(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + struct smiapp_subdev *ssd = to_smiapp_subdev(sd); + struct smiapp_sensor *sensor = ssd->sensor; + u32 mbus_code = + smiapp_csi_data_formats[smiapp_pixel_order(sensor)].code; + unsigned int i; + + mutex_lock(&sensor->mutex); + + for (i = 0; i < ssd->npads; i++) { + struct v4l2_mbus_framefmt *try_fmt = + v4l2_subdev_get_try_format(fh, i); + struct v4l2_rect *try_crop = v4l2_subdev_get_try_crop(fh, i); + struct v4l2_rect *try_comp; + + try_fmt->width = sensor->limits[SMIAPP_LIMIT_X_ADDR_MAX] + 1; + try_fmt->height = sensor->limits[SMIAPP_LIMIT_Y_ADDR_MAX] + 1; + try_fmt->code = mbus_code; + + try_crop->top = 0; + try_crop->left = 0; + try_crop->width = try_fmt->width; + try_crop->height = try_fmt->height; + + if (ssd != sensor->pixel_array) + continue; + + try_comp = v4l2_subdev_get_try_compose(fh, i); + *try_comp = *try_crop; + } + + mutex_unlock(&sensor->mutex); + + return smiapp_set_power(sd, 1); +} + +static int smiapp_close(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh) +{ + return smiapp_set_power(sd, 0); +} + +static const struct v4l2_subdev_video_ops smiapp_video_ops = { + .s_stream = smiapp_set_stream, +}; + +static const struct v4l2_subdev_core_ops smiapp_core_ops = { + .s_power = smiapp_set_power, +}; + +static const struct v4l2_subdev_pad_ops smiapp_pad_ops = { + .enum_mbus_code = smiapp_enum_mbus_code, + .get_fmt = smiapp_get_format, + .set_fmt = smiapp_set_format, + .get_selection = smiapp_get_selection, + .set_selection = smiapp_set_selection, +}; + +static const struct v4l2_subdev_sensor_ops smiapp_sensor_ops = { + .g_skip_frames = smiapp_get_skip_frames, +}; + +static const struct v4l2_subdev_ops smiapp_ops = { + .core = &smiapp_core_ops, + .video = &smiapp_video_ops, + .pad = &smiapp_pad_ops, + .sensor = &smiapp_sensor_ops, +}; + +static const struct media_entity_operations smiapp_entity_ops = { + .link_validate = v4l2_subdev_link_validate, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_src_ops = { + .registered = smiapp_registered, + .open = smiapp_open, + .close = smiapp_close, +}; + +static const struct v4l2_subdev_internal_ops smiapp_internal_ops = { + .open = smiapp_open, + .close = smiapp_close, +}; + +/* ----------------------------------------------------------------------------- + * I2C Driver + */ + +#ifdef CONFIG_PM + +static int smiapp_suspend(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + bool streaming; + + BUG_ON(mutex_is_locked(&sensor->mutex)); + + if (sensor->power_count == 0) + return 0; + + if (sensor->streaming) + smiapp_stop_streaming(sensor); + + streaming = sensor->streaming; + + smiapp_power_off(sensor); + + /* save state for resume */ + sensor->streaming = streaming; + + return 0; +} + +static int smiapp_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + int rval; + + if (sensor->power_count == 0) + return 0; + + rval = smiapp_power_on(sensor); + if (rval) + return rval; + + if (sensor->streaming) + rval = smiapp_start_streaming(sensor); + + return rval; +} + +#else + +#define smiapp_suspend NULL +#define smiapp_resume NULL + +#endif /* CONFIG_PM */ + +static int smiapp_probe(struct i2c_client *client, + const struct i2c_device_id *devid) +{ + struct smiapp_sensor *sensor; + int rval; + + if (client->dev.platform_data == NULL) + return -ENODEV; + + sensor = kzalloc(sizeof(*sensor), GFP_KERNEL); + if (sensor == NULL) + return -ENOMEM; + + sensor->platform_data = client->dev.platform_data; + mutex_init(&sensor->mutex); + mutex_init(&sensor->power_mutex); + sensor->src = &sensor->ssds[sensor->ssds_used]; + + v4l2_i2c_subdev_init(&sensor->src->sd, client, &smiapp_ops); + sensor->src->sd.internal_ops = &smiapp_internal_src_ops; + sensor->src->sd.flags |= V4L2_SUBDEV_FL_HAS_DEVNODE; + sensor->src->sensor = sensor; + + sensor->src->pads[0].flags = MEDIA_PAD_FL_SOURCE; + rval = media_entity_init(&sensor->src->sd.entity, 2, + sensor->src->pads, 0); + if (rval < 0) + kfree(sensor); + + return rval; +} + +static int __exit smiapp_remove(struct i2c_client *client) +{ + struct v4l2_subdev *subdev = i2c_get_clientdata(client); + struct smiapp_sensor *sensor = to_smiapp_sensor(subdev); + unsigned int i; + + if (sensor->power_count) { + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_set_value(sensor->platform_data->xshutdown, 0); + if (sensor->platform_data->set_xclk) + sensor->platform_data->set_xclk(&sensor->src->sd, 0); + else + clk_disable(sensor->ext_clk); + sensor->power_count = 0; + } + + if (sensor->nvm) { + device_remove_file(&client->dev, &dev_attr_nvm); + kfree(sensor->nvm); + } + + for (i = 0; i < sensor->ssds_used; i++) { + media_entity_cleanup(&sensor->ssds[i].sd.entity); + v4l2_device_unregister_subdev(&sensor->ssds[i].sd); + } + smiapp_free_controls(sensor); + if (sensor->platform_data->xshutdown != SMIAPP_NO_XSHUTDOWN) + gpio_free(sensor->platform_data->xshutdown); + if (sensor->ext_clk) + clk_put(sensor->ext_clk); + if (sensor->vana) + regulator_put(sensor->vana); + + kfree(sensor); + + return 0; +} + +static const struct i2c_device_id smiapp_id_table[] = { + { SMIAPP_NAME, 0 }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, smiapp_id_table); + +static const struct dev_pm_ops smiapp_pm_ops = { + .suspend = smiapp_suspend, + .resume = smiapp_resume, +}; + +static struct i2c_driver smiapp_i2c_driver = { + .driver = { + .name = SMIAPP_NAME, + .pm = &smiapp_pm_ops, + }, + .probe = smiapp_probe, + .remove = __exit_p(smiapp_remove), + .id_table = smiapp_id_table, +}; + +module_i2c_driver(smiapp_i2c_driver); + +MODULE_AUTHOR("Sakari Ailus "); +MODULE_DESCRIPTION("Generic SMIA/SMIA++ camera module driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/smiapp/smiapp-limits.c b/drivers/media/video/smiapp/smiapp-limits.c new file mode 100644 index 000000000000..0800e095724e --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-limits.c @@ -0,0 +1,132 @@ +/* + * drivers/media/video/smiapp/smiapp-limits.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include "smiapp.h" + +struct smiapp_reg_limits smiapp_reg_limits[] = { + { SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY, "analogue_gain_capability" }, /* 0 */ + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN, "analogue_gain_code_min" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX, "analogue_gain_code_max" }, + { SMIAPP_REG_U8_THS_ZERO_MIN, "ths_zero_min" }, + { SMIAPP_REG_U8_TCLK_TRAIL_MIN, "tclk_trail_min" }, + { SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY, "integration_time_capability" }, /* 5 */ + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN, "coarse_integration_time_min" }, + { SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN, "coarse_integration_time_max_margin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN, "fine_integration_time_min" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN, "fine_integration_time_max_margin" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY, "digital_gain_capability" }, /* 10 */ + { SMIAPP_REG_U16_DIGITAL_GAIN_MIN, "digital_gain_min" }, + { SMIAPP_REG_U16_DIGITAL_GAIN_MAX, "digital_gain_max" }, + { SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ, "min_ext_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ, "max_ext_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV, "min_pre_pll_clk_div" }, /* 15 */ + { SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV, "max_pre_pll_clk_div" }, + { SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ, "min_pll_ip_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ, "max_pll_ip_freq_hz" }, + { SMIAPP_REG_U16_MIN_PLL_MULTIPLIER, "min_pll_multiplier" }, + { SMIAPP_REG_U16_MAX_PLL_MULTIPLIER, "max_pll_multiplier" }, /* 20 */ + { SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ, "min_pll_op_freq_hz" }, + { SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ, "max_pll_op_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV, "min_vt_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV, "max_vt_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ, "min_vt_sys_clk_freq_hz" }, /* 25 */ + { SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ, "max_vt_sys_clk_freq_hz" }, + { SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ, "min_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ, "max_vt_pix_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV, "min_vt_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV, "max_vt_pix_clk_div" }, /* 30 */ + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES, "min_frame_length_lines" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES, "max_frame_length_lines" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK, "min_line_length_pck" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK, "max_line_length_pck" }, + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK, "min_line_blanking_pck" }, /* 35 */ + { SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES, "min_frame_blanking_lines" }, + { SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE, "min_line_length_pck_step_size" }, + { SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV, "min_op_sys_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV, "max_op_sys_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ, "min_op_sys_clk_freq_hz" }, /* 40 */ + { SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ, "max_op_sys_clk_freq_hz" }, + { SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV, "min_op_pix_clk_div" }, + { SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV, "max_op_pix_clk_div" }, + { SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ, "min_op_pix_clk_freq_hz" }, + { SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ, "max_op_pix_clk_freq_hz" }, /* 45 */ + { SMIAPP_REG_U16_X_ADDR_MIN, "x_addr_min" }, + { SMIAPP_REG_U16_Y_ADDR_MIN, "y_addr_min" }, + { SMIAPP_REG_U16_X_ADDR_MAX, "x_addr_max" }, + { SMIAPP_REG_U16_Y_ADDR_MAX, "y_addr_max" }, + { SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE, "min_x_output_size" }, /* 50 */ + { SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE, "min_y_output_size" }, + { SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE, "max_x_output_size" }, + { SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE, "max_y_output_size" }, + { SMIAPP_REG_U16_MIN_EVEN_INC, "min_even_inc" }, + { SMIAPP_REG_U16_MAX_EVEN_INC, "max_even_inc" }, /* 55 */ + { SMIAPP_REG_U16_MIN_ODD_INC, "min_odd_inc" }, + { SMIAPP_REG_U16_MAX_ODD_INC, "max_odd_inc" }, + { SMIAPP_REG_U16_SCALING_CAPABILITY, "scaling_capability" }, + { SMIAPP_REG_U16_SCALER_M_MIN, "scaler_m_min" }, + { SMIAPP_REG_U16_SCALER_M_MAX, "scaler_m_max" }, /* 60 */ + { SMIAPP_REG_U16_SCALER_N_MIN, "scaler_n_min" }, + { SMIAPP_REG_U16_SCALER_N_MAX, "scaler_n_max" }, + { SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY, "spatial_sampling_capability" }, + { SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY, "digital_crop_capability" }, + { SMIAPP_REG_U16_COMPRESSION_CAPABILITY, "compression_capability" }, /* 65 */ + { SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY, "fifo_support_capability" }, + { SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY, "dphy_ctrl_capability" }, + { SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY, "csi_lane_mode_capability" }, + { SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY, "csi_signalling_mode_capability" }, + { SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY, "fast_standby_capability" }, /* 70 */ + { SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY, "cci_address_control_capability" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS, "max_per_lane_bitrate_1_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS, "max_per_lane_bitrate_2_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS, "max_per_lane_bitrate_3_lane_mode_mbps" }, + { SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS, "max_per_lane_bitrate_4_lane_mode_mbps" }, /* 75 */ + { SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY, "temp_sensor_capability" }, + { SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN, "min_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN, "max_frame_length_lines_bin" }, + { SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN, "min_line_length_pck_bin" }, + { SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN, "max_line_length_pck_bin" }, /* 80 */ + { SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN, "min_line_blanking_pck_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN, "fine_integration_time_min_bin" }, + { SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN, "fine_integration_time_max_margin_bin" }, + { SMIAPP_REG_U8_BINNING_CAPABILITY, "binning_capability" }, + { SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY, "binning_weighting_capability" }, /* 85 */ + { SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY, "data_transfer_if_capability" }, + { SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY, "shading_correction_capability" }, + { SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY, "green_imbalance_capability" }, + { SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY, "black_level_capability" }, + { SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY, "module_specific_correction_capability" }, /* 90 */ + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY, "defect_correction_capability" }, + { SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2, "defect_correction_capability_2" }, + { SMIAPP_REG_U8_EDOF_CAPABILITY, "edof_capability" }, + { SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY, "colour_feedback_capability" }, + { SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY, "estimation_mode_capability" }, /* 95 */ + { SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY, "estimation_zone_capability" }, + { SMIAPP_REG_U16_CAPABILITY_TRDY_MIN, "capability_trdy_min" }, + { SMIAPP_REG_U8_FLASH_MODE_CAPABILITY, "flash_mode_capability" }, + { SMIAPP_REG_U8_ACTUATOR_CAPABILITY, "actuator_capability" }, + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1, "bracketing_lut_capability_1" }, /* 100 */ + { SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2, "bracketing_lut_capability_2" }, + { SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP, "analogue_gain_code_step" }, + { 0, NULL }, +}; diff --git a/drivers/media/video/smiapp/smiapp-limits.h b/drivers/media/video/smiapp/smiapp-limits.h new file mode 100644 index 000000000000..7f4836bb78db --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-limits.h @@ -0,0 +1,128 @@ +/* + * drivers/media/video/smiapp/smiapp-limits.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CAPABILITY 0 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN 1 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX 2 +#define SMIAPP_LIMIT_THS_ZERO_MIN 3 +#define SMIAPP_LIMIT_TCLK_TRAIL_MIN 4 +#define SMIAPP_LIMIT_INTEGRATION_TIME_CAPABILITY 5 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MIN 6 +#define SMIAPP_LIMIT_COARSE_INTEGRATION_TIME_MAX_MARGIN 7 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN 8 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN 9 +#define SMIAPP_LIMIT_DIGITAL_GAIN_CAPABILITY 10 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MIN 11 +#define SMIAPP_LIMIT_DIGITAL_GAIN_MAX 12 +#define SMIAPP_LIMIT_MIN_EXT_CLK_FREQ_HZ 13 +#define SMIAPP_LIMIT_MAX_EXT_CLK_FREQ_HZ 14 +#define SMIAPP_LIMIT_MIN_PRE_PLL_CLK_DIV 15 +#define SMIAPP_LIMIT_MAX_PRE_PLL_CLK_DIV 16 +#define SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ 17 +#define SMIAPP_LIMIT_MAX_PLL_IP_FREQ_HZ 18 +#define SMIAPP_LIMIT_MIN_PLL_MULTIPLIER 19 +#define SMIAPP_LIMIT_MAX_PLL_MULTIPLIER 20 +#define SMIAPP_LIMIT_MIN_PLL_OP_FREQ_HZ 21 +#define SMIAPP_LIMIT_MAX_PLL_OP_FREQ_HZ 22 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_DIV 23 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_DIV 24 +#define SMIAPP_LIMIT_MIN_VT_SYS_CLK_FREQ_HZ 25 +#define SMIAPP_LIMIT_MAX_VT_SYS_CLK_FREQ_HZ 26 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_FREQ_HZ 27 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_FREQ_HZ 28 +#define SMIAPP_LIMIT_MIN_VT_PIX_CLK_DIV 29 +#define SMIAPP_LIMIT_MAX_VT_PIX_CLK_DIV 30 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES 31 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES 32 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK 33 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK 34 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK 35 +#define SMIAPP_LIMIT_MIN_FRAME_BLANKING_LINES 36 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_STEP_SIZE 37 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_DIV 38 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_DIV 39 +#define SMIAPP_LIMIT_MIN_OP_SYS_CLK_FREQ_HZ 40 +#define SMIAPP_LIMIT_MAX_OP_SYS_CLK_FREQ_HZ 41 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_DIV 42 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_DIV 43 +#define SMIAPP_LIMIT_MIN_OP_PIX_CLK_FREQ_HZ 44 +#define SMIAPP_LIMIT_MAX_OP_PIX_CLK_FREQ_HZ 45 +#define SMIAPP_LIMIT_X_ADDR_MIN 46 +#define SMIAPP_LIMIT_Y_ADDR_MIN 47 +#define SMIAPP_LIMIT_X_ADDR_MAX 48 +#define SMIAPP_LIMIT_Y_ADDR_MAX 49 +#define SMIAPP_LIMIT_MIN_X_OUTPUT_SIZE 50 +#define SMIAPP_LIMIT_MIN_Y_OUTPUT_SIZE 51 +#define SMIAPP_LIMIT_MAX_X_OUTPUT_SIZE 52 +#define SMIAPP_LIMIT_MAX_Y_OUTPUT_SIZE 53 +#define SMIAPP_LIMIT_MIN_EVEN_INC 54 +#define SMIAPP_LIMIT_MAX_EVEN_INC 55 +#define SMIAPP_LIMIT_MIN_ODD_INC 56 +#define SMIAPP_LIMIT_MAX_ODD_INC 57 +#define SMIAPP_LIMIT_SCALING_CAPABILITY 58 +#define SMIAPP_LIMIT_SCALER_M_MIN 59 +#define SMIAPP_LIMIT_SCALER_M_MAX 60 +#define SMIAPP_LIMIT_SCALER_N_MIN 61 +#define SMIAPP_LIMIT_SCALER_N_MAX 62 +#define SMIAPP_LIMIT_SPATIAL_SAMPLING_CAPABILITY 63 +#define SMIAPP_LIMIT_DIGITAL_CROP_CAPABILITY 64 +#define SMIAPP_LIMIT_COMPRESSION_CAPABILITY 65 +#define SMIAPP_LIMIT_FIFO_SUPPORT_CAPABILITY 66 +#define SMIAPP_LIMIT_DPHY_CTRL_CAPABILITY 67 +#define SMIAPP_LIMIT_CSI_LANE_MODE_CAPABILITY 68 +#define SMIAPP_LIMIT_CSI_SIGNALLING_MODE_CAPABILITY 69 +#define SMIAPP_LIMIT_FAST_STANDBY_CAPABILITY 70 +#define SMIAPP_LIMIT_CCI_ADDRESS_CONTROL_CAPABILITY 71 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS 72 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS 73 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS 74 +#define SMIAPP_LIMIT_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS 75 +#define SMIAPP_LIMIT_TEMP_SENSOR_CAPABILITY 76 +#define SMIAPP_LIMIT_MIN_FRAME_LENGTH_LINES_BIN 77 +#define SMIAPP_LIMIT_MAX_FRAME_LENGTH_LINES_BIN 78 +#define SMIAPP_LIMIT_MIN_LINE_LENGTH_PCK_BIN 79 +#define SMIAPP_LIMIT_MAX_LINE_LENGTH_PCK_BIN 80 +#define SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN 81 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MIN_BIN 82 +#define SMIAPP_LIMIT_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN 83 +#define SMIAPP_LIMIT_BINNING_CAPABILITY 84 +#define SMIAPP_LIMIT_BINNING_WEIGHTING_CAPABILITY 85 +#define SMIAPP_LIMIT_DATA_TRANSFER_IF_CAPABILITY 86 +#define SMIAPP_LIMIT_SHADING_CORRECTION_CAPABILITY 87 +#define SMIAPP_LIMIT_GREEN_IMBALANCE_CAPABILITY 88 +#define SMIAPP_LIMIT_BLACK_LEVEL_CAPABILITY 89 +#define SMIAPP_LIMIT_MODULE_SPECIFIC_CORRECTION_CAPABILITY 90 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY 91 +#define SMIAPP_LIMIT_DEFECT_CORRECTION_CAPABILITY_2 92 +#define SMIAPP_LIMIT_EDOF_CAPABILITY 93 +#define SMIAPP_LIMIT_COLOUR_FEEDBACK_CAPABILITY 94 +#define SMIAPP_LIMIT_ESTIMATION_MODE_CAPABILITY 95 +#define SMIAPP_LIMIT_ESTIMATION_ZONE_CAPABILITY 96 +#define SMIAPP_LIMIT_CAPABILITY_TRDY_MIN 97 +#define SMIAPP_LIMIT_FLASH_MODE_CAPABILITY 98 +#define SMIAPP_LIMIT_ACTUATOR_CAPABILITY 99 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_1 100 +#define SMIAPP_LIMIT_BRACKETING_LUT_CAPABILITY_2 101 +#define SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_STEP 102 +#define SMIAPP_LIMIT_LAST 103 diff --git a/drivers/media/video/smiapp/smiapp-quirk.c b/drivers/media/video/smiapp/smiapp-quirk.c new file mode 100644 index 000000000000..55e87950dcea --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-quirk.c @@ -0,0 +1,306 @@ +/* + * drivers/media/video/smiapp/smiapp-quirk.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include + +#include "smiapp.h" + +static int smiapp_write_8(struct smiapp_sensor *sensor, u16 reg, u8 val) +{ + return smiapp_write(sensor, (SMIA_REG_8BIT << 16) | reg, val); +} + +static int smiapp_write_8s(struct smiapp_sensor *sensor, + struct smiapp_reg_8 *regs, int len) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + for (; len > 0; len--, regs++) { + rval = smiapp_write_8(sensor, regs->reg, regs->val); + if (rval < 0) { + dev_err(&client->dev, + "error %d writing reg 0x%4.4x, val 0x%2.2x", + rval, regs->reg, regs->val); + return rval; + } + } + + return 0; +} + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + + dev_dbg(&client->dev, "quirk: 0x%8.8x \"%s\" = %d, 0x%x\n", + smiapp_reg_limits[limit].addr, + smiapp_reg_limits[limit].what, val, val); + sensor->limits[limit] = val; +} + +int smiapp_replace_limit_at(struct smiapp_sensor *sensor, + u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int i; + + for (i = 0; smiapp_reg_limits[i].addr; i++) { + if ((smiapp_reg_limits[i].addr & 0xffff) != reg) + continue; + + smiapp_replace_limit(sensor, i, val); + + return 0; + } + + dev_dbg(&client->dev, "quirk: bad register 0x%4.4x\n", reg); + + return -EINVAL; +} + +bool smiapp_quirk_reg(struct smiapp_sensor *sensor, + u32 reg, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + const struct smia_reg *sreg; + + if (!sensor->minfo.quirk) + return false; + + sreg = sensor->minfo.quirk->regs; + + if (!sreg) + return false; + + while (sreg->type) { + u16 type = reg >> 16; + u16 reg16 = reg; + + if (sreg->type != type || sreg->reg != reg16) { + sreg++; + continue; + } + + switch ((u8)type) { + case SMIA_REG_8BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%2.2x\n", + reg, sreg->val); + break; + case SMIA_REG_16BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%4.4x\n", + reg, sreg->val); + break; + case SMIA_REG_32BIT: + dev_dbg(&client->dev, "quirk: 0x%8.8x: 0x%8.8x\n", + reg, sreg->val); + break; + } + + *val = sreg->val; + + return true; + } + + return false; +} + +static int jt8ew9_limits(struct smiapp_sensor *sensor) +{ + if (sensor->minfo.revision_number_major < 0x03) + sensor->frame_skip = 1; + + /* Below 24 gain doesn't have effect at all, */ + /* but ~59 is needed for full dynamic range */ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MIN, 59); + smiapp_replace_limit( + sensor, SMIAPP_LIMIT_ANALOGUE_GAIN_CODE_MAX, 6000); + + return 0; +} + +static int jt8ew9_post_poweron(struct smiapp_sensor *sensor) +{ + struct smiapp_reg_8 regs[] = { + { 0x30a3, 0xd8 }, /* Output port control : LVDS ports only */ + { 0x30ae, 0x00 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x30af, 0xd0 }, /* 0x0307 pll_multiplier maximum value on PLL input 9.6MHz ( 19.2MHz is divided on pre_pll_div) */ + { 0x322d, 0x04 }, /* Adjusting Processing Image Size to Scaler Toshiba Recommendation Setting */ + { 0x3255, 0x0f }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3256, 0x15 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + { 0x3258, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3259, 0x70 }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x325f, 0x7c }, /* Analog Gain Control Toshiba Recommendation Setting */ + { 0x3302, 0x06 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3304, 0x00 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3307, 0x22 }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x3308, 0x8d }, /* Pixel Reference Voltage Control Toshiba Recommendation Setting */ + { 0x331e, 0x0f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3320, 0x30 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3321, 0x11 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3322, 0x98 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3323, 0x64 }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x3325, 0x83 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3330, 0x18 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x333c, 0x01 }, /* Read Out Timing Control Toshiba Recommendation Setting */ + { 0x3345, 0x2f }, /* Black Hole Sun Correction Control Toshiba Recommendation Setting */ + { 0x33de, 0x38 }, /* Horizontal Noise Reduction Control Toshiba Recommendation Setting */ + /* Taken from v03. No idea what the rest are. */ + { 0x32e0, 0x05 }, + { 0x32e1, 0x05 }, + { 0x32e2, 0x04 }, + { 0x32e5, 0x04 }, + { 0x32e6, 0x04 }, + + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_jt8ew9_quirk = { + .limits = jt8ew9_limits, + .post_poweron = jt8ew9_post_poweron, +}; + +static int imx125es_post_poweron(struct smiapp_sensor *sensor) +{ + /* Taken from v02. No idea what the other two are. */ + struct smiapp_reg_8 regs[] = { + /* + * 0x3302: clk during frame blanking: + * 0x00 - HS mode, 0x01 - LP11 + */ + { 0x3302, 0x01 }, + { 0x302d, 0x00 }, + { 0x3b08, 0x8c }, + }; + + return smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); +} + +const struct smiapp_quirk smiapp_imx125es_quirk = { + .post_poweron = imx125es_post_poweron, +}; + +static int jt8ev1_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_X_ADDR_MAX, 4271); + smiapp_replace_limit(sensor, + SMIAPP_LIMIT_MIN_LINE_BLANKING_PCK_BIN, 184); + + return 0; +} + +static int jt8ev1_post_poweron(struct smiapp_sensor *sensor) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + int rval; + + struct smiapp_reg_8 regs[] = { + { 0x3031, 0xcd }, /* For digital binning (EQ_MONI) */ + { 0x30a3, 0xd0 }, /* FLASH STROBE enable */ + { 0x3237, 0x00 }, /* For control of pulse timing for ADC */ + { 0x3238, 0x43 }, + { 0x3301, 0x06 }, /* For analog bias for sensor */ + { 0x3302, 0x06 }, + { 0x3304, 0x00 }, + { 0x3305, 0x88 }, + { 0x332a, 0x14 }, + { 0x332c, 0x6b }, + { 0x3336, 0x01 }, + { 0x333f, 0x1f }, + { 0x3355, 0x00 }, + { 0x3356, 0x20 }, + { 0x33bf, 0x20 }, /* Adjust the FBC speed */ + { 0x33c9, 0x20 }, + { 0x33ce, 0x30 }, /* Adjust the parameter for logic function */ + { 0x33cf, 0xec }, /* For Black sun */ + { 0x3328, 0x80 }, /* Ugh. No idea what's this. */ + }; + + struct smiapp_reg_8 regs_96[] = { + { 0x30ae, 0x00 }, /* For control of ADC clock */ + { 0x30af, 0xd0 }, + { 0x30b0, 0x01 }, + }; + + rval = smiapp_write_8s(sensor, regs, ARRAY_SIZE(regs)); + if (rval < 0) + return rval; + + switch (sensor->platform_data->ext_clk) { + case 9600000: + return smiapp_write_8s(sensor, regs_96, + ARRAY_SIZE(regs_96)); + default: + dev_warn(&client->dev, "no MSRs for %d Hz ext_clk\n", + sensor->platform_data->ext_clk); + return 0; + } +} + +static int jt8ev1_pre_streamon(struct smiapp_sensor *sensor) +{ + return smiapp_write_8(sensor, 0x3328, 0x00); +} + +static int jt8ev1_post_streamoff(struct smiapp_sensor *sensor) +{ + int rval; + + /* Workaround: allows fast standby to work properly */ + rval = smiapp_write_8(sensor, 0x3205, 0x04); + if (rval < 0) + return rval; + + /* Wait for 1 ms + one line => 2 ms is likely enough */ + usleep_range(2000, 2000); + + /* Restore it */ + rval = smiapp_write_8(sensor, 0x3205, 0x00); + if (rval < 0) + return rval; + + return smiapp_write_8(sensor, 0x3328, 0x80); +} + +const struct smiapp_quirk smiapp_jt8ev1_quirk = { + .limits = jt8ev1_limits, + .post_poweron = jt8ev1_post_poweron, + .pre_streamon = jt8ev1_pre_streamon, + .post_streamoff = jt8ev1_post_streamoff, + .flags = SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE, +}; + +static int tcm8500md_limits(struct smiapp_sensor *sensor) +{ + smiapp_replace_limit(sensor, SMIAPP_LIMIT_MIN_PLL_IP_FREQ_HZ, 2700000); + + return 0; +} + +const struct smiapp_quirk smiapp_tcm8500md_quirk = { + .limits = tcm8500md_limits, +}; diff --git a/drivers/media/video/smiapp/smiapp-quirk.h b/drivers/media/video/smiapp/smiapp-quirk.h new file mode 100644 index 000000000000..f4dcaabaefe7 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-quirk.h @@ -0,0 +1,83 @@ +/* + * drivers/media/video/smiapp/smiapp-quirk.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_QUIRK__ +#define __SMIAPP_QUIRK__ + +struct smiapp_sensor; + +/** + * struct smiapp_quirk - quirks for sensors that deviate from SMIA++ standard + * + * @limits: Replace sensor->limits with values which can't be read from + * sensor registers. Called the first time the sensor is powered up. + * @post_poweron: Called always after the sensor has been fully powered on. + * @pre_streamon: Called just before streaming is enabled. + * @post_streamon: Called right after stopping streaming. + */ +struct smiapp_quirk { + int (*limits)(struct smiapp_sensor *sensor); + int (*post_poweron)(struct smiapp_sensor *sensor); + int (*pre_streamon)(struct smiapp_sensor *sensor); + int (*post_streamoff)(struct smiapp_sensor *sensor); + const struct smia_reg *regs; + unsigned long flags; +}; + +/* op pix clock is for all lanes in total normally */ +#define SMIAPP_QUIRK_FLAG_OP_PIX_CLOCK_PER_LANE (1 << 0) +#define SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY (1 << 1) + +struct smiapp_reg_8 { + u16 reg; + u8 val; +}; + +void smiapp_replace_limit(struct smiapp_sensor *sensor, + u32 limit, u32 val); +bool smiapp_quirk_reg(struct smiapp_sensor *sensor, + u32 reg, u32 *val); + +#define SMIAPP_MK_QUIRK_REG(_reg, _val) \ + { \ + .type = (_reg >> 16), \ + .reg = (u16)_reg, \ + .val = _val, \ + } + +#define smiapp_call_quirk(_sensor, _quirk, ...) \ + (_sensor->minfo.quirk && \ + _sensor->minfo.quirk->_quirk ? \ + _sensor->minfo.quirk->_quirk(_sensor, ##__VA_ARGS__) : 0) + +#define smiapp_needs_quirk(_sensor, _quirk) \ + (_sensor->minfo.quirk ? \ + _sensor->minfo.quirk->flags & _quirk : 0) + +extern const struct smiapp_quirk smiapp_jt8ev1_quirk; +extern const struct smiapp_quirk smiapp_imx125es_quirk; +extern const struct smiapp_quirk smiapp_jt8ew9_quirk; +extern const struct smiapp_quirk smiapp_tcm8500md_quirk; + +#endif /* __SMIAPP_QUIRK__ */ diff --git a/drivers/media/video/smiapp/smiapp-reg-defs.h b/drivers/media/video/smiapp/smiapp-reg-defs.h new file mode 100644 index 000000000000..a089eb8161e1 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-reg-defs.h @@ -0,0 +1,503 @@ +/* + * drivers/media/video/smiapp/smiapp-reg-defs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ +#define SMIAPP_REG_MK_U8(r) ((SMIA_REG_8BIT << 16) | (r)) +#define SMIAPP_REG_MK_U16(r) ((SMIA_REG_16BIT << 16) | (r)) +#define SMIAPP_REG_MK_U32(r) ((SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_MK_F32(r) (SMIA_REG_FLAG_FLOAT | (SMIA_REG_32BIT << 16) | (r)) + +#define SMIAPP_REG_U16_MODEL_ID SMIAPP_REG_MK_U16(0x0000) +#define SMIAPP_REG_U8_REVISION_NUMBER_MAJOR SMIAPP_REG_MK_U8(0x0002) +#define SMIAPP_REG_U8_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0003) +#define SMIAPP_REG_U8_SMIA_VERSION SMIAPP_REG_MK_U8(0x0004) +#define SMIAPP_REG_U8_FRAME_COUNT SMIAPP_REG_MK_U8(0x0005) +#define SMIAPP_REG_U8_PIXEL_ORDER SMIAPP_REG_MK_U8(0x0006) +#define SMIAPP_REG_U16_DATA_PEDESTAL SMIAPP_REG_MK_U16(0x0008) +#define SMIAPP_REG_U8_PIXEL_DEPTH SMIAPP_REG_MK_U8(0x000c) +#define SMIAPP_REG_U8_REVISION_NUMBER_MINOR SMIAPP_REG_MK_U8(0x0010) +#define SMIAPP_REG_U8_SMIAPP_VERSION SMIAPP_REG_MK_U8(0x0011) +#define SMIAPP_REG_U8_MODULE_DATE_YEAR SMIAPP_REG_MK_U8(0x0012) +#define SMIAPP_REG_U8_MODULE_DATE_MONTH SMIAPP_REG_MK_U8(0x0013) +#define SMIAPP_REG_U8_MODULE_DATE_DAY SMIAPP_REG_MK_U8(0x0014) +#define SMIAPP_REG_U8_MODULE_DATE_PHASE SMIAPP_REG_MK_U8(0x0015) +#define SMIAPP_REG_U16_SENSOR_MODEL_ID SMIAPP_REG_MK_U16(0x0016) +#define SMIAPP_REG_U8_SENSOR_REVISION_NUMBER SMIAPP_REG_MK_U8(0x0018) +#define SMIAPP_REG_U8_SENSOR_MANUFACTURER_ID SMIAPP_REG_MK_U8(0x0019) +#define SMIAPP_REG_U8_SENSOR_FIRMWARE_VERSION SMIAPP_REG_MK_U8(0x001a) +#define SMIAPP_REG_U32_SERIAL_NUMBER SMIAPP_REG_MK_U32(0x001c) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x0040) +#define SMIAPP_REG_U8_FRAME_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x0041) +#define SMIAPP_REG_U16_FRAME_FORMAT_DESCRIPTOR_2(n) SMIAPP_REG_MK_U16(0x0042 + ((n) << 1)) /* 0 <= n <= 14 */ +#define SMIAPP_REG_U32_FRAME_FORMAT_DESCRIPTOR_4(n) SMIAPP_REG_MK_U32(0x0060 + ((n) << 2)) /* 0 <= n <= 7 */ +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x0080) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MIN SMIAPP_REG_MK_U16(0x0084) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_MAX SMIAPP_REG_MK_U16(0x0086) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_STEP SMIAPP_REG_MK_U16(0x0088) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_TYPE SMIAPP_REG_MK_U16(0x008a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M0 SMIAPP_REG_MK_U16(0x008c) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C0 SMIAPP_REG_MK_U16(0x008e) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_M1 SMIAPP_REG_MK_U16(0x0090) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_C1 SMIAPP_REG_MK_U16(0x0092) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_TYPE SMIAPP_REG_MK_U8(0x00c0) +#define SMIAPP_REG_U8_DATA_FORMAT_MODEL_SUBTYPE SMIAPP_REG_MK_U8(0x00c1) +#define SMIAPP_REG_U16_DATA_FORMAT_DESCRIPTOR(n) SMIAPP_REG_MK_U16(0x00c2 + ((n) << 1)) +#define SMIAPP_REG_U8_MODE_SELECT SMIAPP_REG_MK_U8(0x0100) +#define SMIAPP_REG_U8_IMAGE_ORIENTATION SMIAPP_REG_MK_U8(0x0101) +#define SMIAPP_REG_U8_SOFTWARE_RESET SMIAPP_REG_MK_U8(0x0103) +#define SMIAPP_REG_U8_GROUPED_PARAMETER_HOLD SMIAPP_REG_MK_U8(0x0104) +#define SMIAPP_REG_U8_MASK_CORRUPTED_FRAMES SMIAPP_REG_MK_U8(0x0105) +#define SMIAPP_REG_U8_FAST_STANDBY_CTRL SMIAPP_REG_MK_U8(0x0106) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0107) +#define SMIAPP_REG_U8_2ND_CCI_IF_CONTROL SMIAPP_REG_MK_U8(0x0108) +#define SMIAPP_REG_U8_2ND_CCI_ADDRESS_CONTROL SMIAPP_REG_MK_U8(0x0109) +#define SMIAPP_REG_U8_CSI_CHANNEL_IDENTIFIER SMIAPP_REG_MK_U8(0x0110) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE SMIAPP_REG_MK_U8(0x0111) +#define SMIAPP_REG_U16_CSI_DATA_FORMAT SMIAPP_REG_MK_U16(0x0112) +#define SMIAPP_REG_U8_CSI_LANE_MODE SMIAPP_REG_MK_U8(0x0114) +#define SMIAPP_REG_U8_CSI2_10_TO_8_DT SMIAPP_REG_MK_U8(0x0115) +#define SMIAPP_REG_U8_CSI2_10_TO_7_DT SMIAPP_REG_MK_U8(0x0116) +#define SMIAPP_REG_U8_CSI2_10_TO_6_DT SMIAPP_REG_MK_U8(0x0117) +#define SMIAPP_REG_U8_CSI2_12_TO_8_DT SMIAPP_REG_MK_U8(0x0118) +#define SMIAPP_REG_U8_CSI2_12_TO_7_DT SMIAPP_REG_MK_U8(0x0119) +#define SMIAPP_REG_U8_CSI2_12_TO_6_DT SMIAPP_REG_MK_U8(0x011a) +#define SMIAPP_REG_U8_CSI2_14_TO_10_DT SMIAPP_REG_MK_U8(0x011b) +#define SMIAPP_REG_U8_CSI2_14_TO_8_DT SMIAPP_REG_MK_U8(0x011c) +#define SMIAPP_REG_U8_CSI2_16_TO_10_DT SMIAPP_REG_MK_U8(0x011d) +#define SMIAPP_REG_U8_CSI2_16_TO_8_DT SMIAPP_REG_MK_U8(0x011e) +#define SMIAPP_REG_U8_GAIN_MODE SMIAPP_REG_MK_U8(0x0120) +#define SMIAPP_REG_U16_VANA_VOLTAGE SMIAPP_REG_MK_U16(0x0130) +#define SMIAPP_REG_U16_VDIG_VOLTAGE SMIAPP_REG_MK_U16(0x0132) +#define SMIAPP_REG_U16_VIO_VOLTAGE SMIAPP_REG_MK_U16(0x0134) +#define SMIAPP_REG_U16_EXTCLK_FREQUENCY_MHZ SMIAPP_REG_MK_U16(0x0136) +#define SMIAPP_REG_U8_TEMP_SENSOR_CONTROL SMIAPP_REG_MK_U8(0x0138) +#define SMIAPP_REG_U8_TEMP_SENSOR_MODE SMIAPP_REG_MK_U8(0x0139) +#define SMIAPP_REG_U8_TEMP_SENSOR_OUTPUT SMIAPP_REG_MK_U8(0x013a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0200) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME SMIAPP_REG_MK_U16(0x0202) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GLOBAL SMIAPP_REG_MK_U16(0x0204) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENR SMIAPP_REG_MK_U16(0x0206) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_RED SMIAPP_REG_MK_U16(0x0208) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_BLUE SMIAPP_REG_MK_U16(0x020a) +#define SMIAPP_REG_U16_ANALOGUE_GAIN_CODE_GREENB SMIAPP_REG_MK_U16(0x020c) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENR SMIAPP_REG_MK_U16(0x020e) +#define SMIAPP_REG_U16_DIGITAL_GAIN_RED SMIAPP_REG_MK_U16(0x0210) +#define SMIAPP_REG_U16_DIGITAL_GAIN_BLUE SMIAPP_REG_MK_U16(0x0212) +#define SMIAPP_REG_U16_DIGITAL_GAIN_GREENB SMIAPP_REG_MK_U16(0x0214) +#define SMIAPP_REG_U16_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0300) +#define SMIAPP_REG_U16_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x0302) +#define SMIAPP_REG_U16_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x0304) +#define SMIAPP_REG_U16_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x0306) +#define SMIAPP_REG_U16_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x0308) +#define SMIAPP_REG_U16_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x030a) +#define SMIAPP_REG_U16_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x0340) +#define SMIAPP_REG_U16_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x0342) +#define SMIAPP_REG_U16_X_ADDR_START SMIAPP_REG_MK_U16(0x0344) +#define SMIAPP_REG_U16_Y_ADDR_START SMIAPP_REG_MK_U16(0x0346) +#define SMIAPP_REG_U16_X_ADDR_END SMIAPP_REG_MK_U16(0x0348) +#define SMIAPP_REG_U16_Y_ADDR_END SMIAPP_REG_MK_U16(0x034a) +#define SMIAPP_REG_U16_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034c) +#define SMIAPP_REG_U16_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x034e) +#define SMIAPP_REG_U16_X_EVEN_INC SMIAPP_REG_MK_U16(0x0380) +#define SMIAPP_REG_U16_X_ODD_INC SMIAPP_REG_MK_U16(0x0382) +#define SMIAPP_REG_U16_Y_EVEN_INC SMIAPP_REG_MK_U16(0x0384) +#define SMIAPP_REG_U16_Y_ODD_INC SMIAPP_REG_MK_U16(0x0386) +#define SMIAPP_REG_U16_SCALING_MODE SMIAPP_REG_MK_U16(0x0400) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING SMIAPP_REG_MK_U16(0x0402) +#define SMIAPP_REG_U16_SCALE_M SMIAPP_REG_MK_U16(0x0404) +#define SMIAPP_REG_U16_SCALE_N SMIAPP_REG_MK_U16(0x0406) +#define SMIAPP_REG_U16_DIGITAL_CROP_X_OFFSET SMIAPP_REG_MK_U16(0x0408) +#define SMIAPP_REG_U16_DIGITAL_CROP_Y_OFFSET SMIAPP_REG_MK_U16(0x040a) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_WIDTH SMIAPP_REG_MK_U16(0x040c) +#define SMIAPP_REG_U16_DIGITAL_CROP_IMAGE_HEIGHT SMIAPP_REG_MK_U16(0x040e) +#define SMIAPP_REG_U16_COMPRESSION_MODE SMIAPP_REG_MK_U16(0x0500) +#define SMIAPP_REG_U16_TEST_PATTERN_MODE SMIAPP_REG_MK_U16(0x0600) +#define SMIAPP_REG_U16_TEST_DATA_RED SMIAPP_REG_MK_U16(0x0602) +#define SMIAPP_REG_U16_TEST_DATA_GREENR SMIAPP_REG_MK_U16(0x0604) +#define SMIAPP_REG_U16_TEST_DATA_BLUE SMIAPP_REG_MK_U16(0x0606) +#define SMIAPP_REG_U16_TEST_DATA_GREENB SMIAPP_REG_MK_U16(0x0608) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060a) +#define SMIAPP_REG_U16_HORIZONTAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x060c) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_WIDTH SMIAPP_REG_MK_U16(0x060e) +#define SMIAPP_REG_U16_VERTICAL_CURSOR_POSITION SMIAPP_REG_MK_U16(0x0610) +#define SMIAPP_REG_U16_FIFO_WATER_MARK_PIXELS SMIAPP_REG_MK_U16(0x0700) +#define SMIAPP_REG_U8_TCLK_POST SMIAPP_REG_MK_U8(0x0800) +#define SMIAPP_REG_U8_THS_PREPARE SMIAPP_REG_MK_U8(0x0801) +#define SMIAPP_REG_U8_THS_ZERO_MIN SMIAPP_REG_MK_U8(0x0802) +#define SMIAPP_REG_U8_THS_TRAIL SMIAPP_REG_MK_U8(0x0803) +#define SMIAPP_REG_U8_TCLK_TRAIL_MIN SMIAPP_REG_MK_U8(0x0804) +#define SMIAPP_REG_U8_TCLK_PREPARE SMIAPP_REG_MK_U8(0x0805) +#define SMIAPP_REG_U8_TCLK_ZERO SMIAPP_REG_MK_U8(0x0806) +#define SMIAPP_REG_U8_TLPX SMIAPP_REG_MK_U8(0x0807) +#define SMIAPP_REG_U8_DPHY_CTRL SMIAPP_REG_MK_U8(0x0808) +#define SMIAPP_REG_U32_REQUESTED_LINK_BIT_RATE_MBPS SMIAPP_REG_MK_U32(0x0820) +#define SMIAPP_REG_U8_BINNING_MODE SMIAPP_REG_MK_U8(0x0900) +#define SMIAPP_REG_U8_BINNING_TYPE SMIAPP_REG_MK_U8(0x0901) +#define SMIAPP_REG_U8_BINNING_WEIGHTING SMIAPP_REG_MK_U8(0x0902) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_CTRL SMIAPP_REG_MK_U8(0x0a00) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_STATUS SMIAPP_REG_MK_U8(0x0a01) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a02) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_0 SMIAPP_REG_MK_U8(0x0a04) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_1 SMIAPP_REG_MK_U8(0x0a05) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_2 SMIAPP_REG_MK_U8(0x0a06) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_3 SMIAPP_REG_MK_U8(0x0a07) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_4 SMIAPP_REG_MK_U8(0x0a08) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_5 SMIAPP_REG_MK_U8(0x0a09) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_12 SMIAPP_REG_MK_U8(0x0a10) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_13 SMIAPP_REG_MK_U8(0x0a11) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_14 SMIAPP_REG_MK_U8(0x0a12) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_15 SMIAPP_REG_MK_U8(0x0a13) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_16 SMIAPP_REG_MK_U8(0x0a14) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_17 SMIAPP_REG_MK_U8(0x0a15) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_18 SMIAPP_REG_MK_U8(0x0a16) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_19 SMIAPP_REG_MK_U8(0x0a17) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_20 SMIAPP_REG_MK_U8(0x0a18) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_21 SMIAPP_REG_MK_U8(0x0a19) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_22 SMIAPP_REG_MK_U8(0x0a1a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_23 SMIAPP_REG_MK_U8(0x0a1b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_24 SMIAPP_REG_MK_U8(0x0a1c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_25 SMIAPP_REG_MK_U8(0x0a1d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_26 SMIAPP_REG_MK_U8(0x0a1e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_27 SMIAPP_REG_MK_U8(0x0a1f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_28 SMIAPP_REG_MK_U8(0x0a20) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_29 SMIAPP_REG_MK_U8(0x0a21) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_30 SMIAPP_REG_MK_U8(0x0a22) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_31 SMIAPP_REG_MK_U8(0x0a23) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_32 SMIAPP_REG_MK_U8(0x0a24) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_33 SMIAPP_REG_MK_U8(0x0a25) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_34 SMIAPP_REG_MK_U8(0x0a26) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_35 SMIAPP_REG_MK_U8(0x0a27) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_36 SMIAPP_REG_MK_U8(0x0a28) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_37 SMIAPP_REG_MK_U8(0x0a29) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_38 SMIAPP_REG_MK_U8(0x0a2a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_39 SMIAPP_REG_MK_U8(0x0a2b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_40 SMIAPP_REG_MK_U8(0x0a2c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_41 SMIAPP_REG_MK_U8(0x0a2d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_42 SMIAPP_REG_MK_U8(0x0a2e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_43 SMIAPP_REG_MK_U8(0x0a2f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_44 SMIAPP_REG_MK_U8(0x0a30) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_45 SMIAPP_REG_MK_U8(0x0a31) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_46 SMIAPP_REG_MK_U8(0x0a32) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_47 SMIAPP_REG_MK_U8(0x0a33) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_48 SMIAPP_REG_MK_U8(0x0a34) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_49 SMIAPP_REG_MK_U8(0x0a35) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_50 SMIAPP_REG_MK_U8(0x0a36) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_51 SMIAPP_REG_MK_U8(0x0a37) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_52 SMIAPP_REG_MK_U8(0x0a38) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_53 SMIAPP_REG_MK_U8(0x0a39) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_54 SMIAPP_REG_MK_U8(0x0a3a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_55 SMIAPP_REG_MK_U8(0x0a3b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_56 SMIAPP_REG_MK_U8(0x0a3c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_57 SMIAPP_REG_MK_U8(0x0a3d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_58 SMIAPP_REG_MK_U8(0x0a3e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_59 SMIAPP_REG_MK_U8(0x0a3f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_60 SMIAPP_REG_MK_U8(0x0a40) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_61 SMIAPP_REG_MK_U8(0x0a41) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_62 SMIAPP_REG_MK_U8(0x0a42) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_1_DATA_63 SMIAPP_REG_MK_U8(0x0a43) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_CTRL SMIAPP_REG_MK_U8(0x0a44) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_STATUS SMIAPP_REG_MK_U8(0x0a45) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_PAGE_SELECT SMIAPP_REG_MK_U8(0x0a46) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_0 SMIAPP_REG_MK_U8(0x0a48) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_1 SMIAPP_REG_MK_U8(0x0a49) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_2 SMIAPP_REG_MK_U8(0x0a4a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_3 SMIAPP_REG_MK_U8(0x0a4b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_4 SMIAPP_REG_MK_U8(0x0a4c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_5 SMIAPP_REG_MK_U8(0x0a4d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_6 SMIAPP_REG_MK_U8(0x0a4e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_7 SMIAPP_REG_MK_U8(0x0a4f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_8 SMIAPP_REG_MK_U8(0x0a50) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_9 SMIAPP_REG_MK_U8(0x0a51) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_10 SMIAPP_REG_MK_U8(0x0a52) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_11 SMIAPP_REG_MK_U8(0x0a53) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_12 SMIAPP_REG_MK_U8(0x0a54) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_13 SMIAPP_REG_MK_U8(0x0a55) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_14 SMIAPP_REG_MK_U8(0x0a56) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_15 SMIAPP_REG_MK_U8(0x0a57) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_16 SMIAPP_REG_MK_U8(0x0a58) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_17 SMIAPP_REG_MK_U8(0x0a59) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_18 SMIAPP_REG_MK_U8(0x0a5a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_19 SMIAPP_REG_MK_U8(0x0a5b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_20 SMIAPP_REG_MK_U8(0x0a5c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_21 SMIAPP_REG_MK_U8(0x0a5d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_22 SMIAPP_REG_MK_U8(0x0a5e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_23 SMIAPP_REG_MK_U8(0x0a5f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_24 SMIAPP_REG_MK_U8(0x0a60) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_25 SMIAPP_REG_MK_U8(0x0a61) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_26 SMIAPP_REG_MK_U8(0x0a62) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_27 SMIAPP_REG_MK_U8(0x0a63) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_28 SMIAPP_REG_MK_U8(0x0a64) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_29 SMIAPP_REG_MK_U8(0x0a65) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_30 SMIAPP_REG_MK_U8(0x0a66) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_31 SMIAPP_REG_MK_U8(0x0a67) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_32 SMIAPP_REG_MK_U8(0x0a68) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_33 SMIAPP_REG_MK_U8(0x0a69) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_34 SMIAPP_REG_MK_U8(0x0a6a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_35 SMIAPP_REG_MK_U8(0x0a6b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_36 SMIAPP_REG_MK_U8(0x0a6c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_37 SMIAPP_REG_MK_U8(0x0a6d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_38 SMIAPP_REG_MK_U8(0x0a6e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_39 SMIAPP_REG_MK_U8(0x0a6f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_40 SMIAPP_REG_MK_U8(0x0a70) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_41 SMIAPP_REG_MK_U8(0x0a71) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_42 SMIAPP_REG_MK_U8(0x0a72) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_43 SMIAPP_REG_MK_U8(0x0a73) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_44 SMIAPP_REG_MK_U8(0x0a74) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_45 SMIAPP_REG_MK_U8(0x0a75) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_46 SMIAPP_REG_MK_U8(0x0a76) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_47 SMIAPP_REG_MK_U8(0x0a77) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_48 SMIAPP_REG_MK_U8(0x0a78) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_49 SMIAPP_REG_MK_U8(0x0a79) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_50 SMIAPP_REG_MK_U8(0x0a7a) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_51 SMIAPP_REG_MK_U8(0x0a7b) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_52 SMIAPP_REG_MK_U8(0x0a7c) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_53 SMIAPP_REG_MK_U8(0x0a7d) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_54 SMIAPP_REG_MK_U8(0x0a7e) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_55 SMIAPP_REG_MK_U8(0x0a7f) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_56 SMIAPP_REG_MK_U8(0x0a80) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_57 SMIAPP_REG_MK_U8(0x0a81) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_58 SMIAPP_REG_MK_U8(0x0a82) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_59 SMIAPP_REG_MK_U8(0x0a83) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_60 SMIAPP_REG_MK_U8(0x0a84) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_61 SMIAPP_REG_MK_U8(0x0a85) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_62 SMIAPP_REG_MK_U8(0x0a86) +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_2_DATA_63 SMIAPP_REG_MK_U8(0x0a87) +#define SMIAPP_REG_U8_SHADING_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b00) +#define SMIAPP_REG_U8_LUMINANCE_CORRECTION_LEVEL SMIAPP_REG_MK_U8(0x0b01) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_ENABLE SMIAPP_REG_MK_U8(0x0b02) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_FILTER_WEIGHT SMIAPP_REG_MK_U8(0x0b03) +#define SMIAPP_REG_U8_BLACK_LEVEL_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b04) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b05) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b06) +#define SMIAPP_REG_U8_SINGLE_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b07) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b08) +#define SMIAPP_REG_U8_DYNAMIC_COUPLET_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b09) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0a) +#define SMIAPP_REG_U8_COMBINED_DEFECT_CORRECT_WEIGHT SMIAPP_REG_MK_U8(0x0b0b) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_ENABLE SMIAPP_REG_MK_U8(0x0b0c) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_WEIGHT SMIAPP_REG_MK_U8(0x0b0d) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b0e) +#define SMIAPP_REG_U8_MAPPED_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b0f) +#define SMIAPP_REG_U8_MAPPED_COUPLET_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b10) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b11) +#define SMIAPP_REG_U8_MAPPED_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b12) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b13) +#define SMIAPP_REG_U8_DYNAMIC_TRIPLET_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b14) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ENABLE SMIAPP_REG_MK_U8(0x0b15) +#define SMIAPP_REG_U8_DYNAMIC_LINE_DEFECT_CORRECT_ADJUST SMIAPP_REG_MK_U8(0x0b16) +#define SMIAPP_REG_U8_EDOF_MODE SMIAPP_REG_MK_U8(0x0b80) +#define SMIAPP_REG_U8_SHARPNESS SMIAPP_REG_MK_U8(0x0b83) +#define SMIAPP_REG_U8_DENOISING SMIAPP_REG_MK_U8(0x0b84) +#define SMIAPP_REG_U8_MODULE_SPECIFIC SMIAPP_REG_MK_U8(0x0b85) +#define SMIAPP_REG_U16_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x0b86) +#define SMIAPP_REG_U16_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x0b88) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CTRL SMIAPP_REG_MK_U8(0x0b8a) +#define SMIAPP_REG_U16_COLOUR_TEMPERATURE SMIAPP_REG_MK_U16(0x0b8c) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENR SMIAPP_REG_MK_U16(0x0b8e) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_RED SMIAPP_REG_MK_U16(0x0b90) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_BLUE SMIAPP_REG_MK_U16(0x0b92) +#define SMIAPP_REG_U16_ABSOLUTE_GAIN_GREENB SMIAPP_REG_MK_U16(0x0b94) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_MODE SMIAPP_REG_MK_U8(0x0bc0) +#define SMIAPP_REG_U16_FIXED_ZONE_WEIGHTING SMIAPP_REG_MK_U16(0x0bc2) +#define SMIAPP_REG_U16_CUSTOM_ZONE_X_START SMIAPP_REG_MK_U16(0x0bc4) +#define SMIAPP_REG_U16_CUSTOM_ZONE_Y_START SMIAPP_REG_MK_U16(0x0bc6) +#define SMIAPP_REG_U16_CUSTOM_ZONE_WIDTH SMIAPP_REG_MK_U16(0x0bc8) +#define SMIAPP_REG_U16_CUSTOM_ZONE_HEIGHT SMIAPP_REG_MK_U16(0x0bca) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL1 SMIAPP_REG_MK_U8(0x0c00) +#define SMIAPP_REG_U8_GLOBAL_RESET_CTRL2 SMIAPP_REG_MK_U8(0x0c01) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_1 SMIAPP_REG_MK_U8(0x0c02) +#define SMIAPP_REG_U8_GLOBAL_RESET_MODE_CONFIG_2 SMIAPP_REG_MK_U8(0x0c03) +#define SMIAPP_REG_U16_TRDY_CTRL SMIAPP_REG_MK_U16(0x0c04) +#define SMIAPP_REG_U16_TRDOUT_CTRL SMIAPP_REG_MK_U16(0x0c06) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c08) +#define SMIAPP_REG_U16_TSHUTTER_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c0a) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c0c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c0e) +#define SMIAPP_REG_U16_TGRST_INTERVAL_CTRL SMIAPP_REG_MK_U16(0x0c10) +#define SMIAPP_REG_U8_FLASH_STROBE_ADJUSTMENT SMIAPP_REG_MK_U8(0x0c12) +#define SMIAPP_REG_U16_FLASH_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c14) +#define SMIAPP_REG_U16_TFLASH_STROBE_DELAY_RS_CTRL SMIAPP_REG_MK_U16(0x0c16) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c18) +#define SMIAPP_REG_U8_FLASH_MODE_RS SMIAPP_REG_MK_U8(0x0c1a) +#define SMIAPP_REG_U8_FLASH_TRIGGER_RS SMIAPP_REG_MK_U8(0x0c1b) +#define SMIAPP_REG_U8_FLASH_STATUS SMIAPP_REG_MK_U8(0x0c1c) +#define SMIAPP_REG_U8_SA_STROBE_MODE SMIAPP_REG_MK_U8(0x0c1d) +#define SMIAPP_REG_U16_SA_STROBE_START_POINT SMIAPP_REG_MK_U16(0x0c1e) +#define SMIAPP_REG_U16_TSA_STROBE_DELAY_CTRL SMIAPP_REG_MK_U16(0x0c20) +#define SMIAPP_REG_U16_TSA_STROBE_WIDTH_CTRL SMIAPP_REG_MK_U16(0x0c22) +#define SMIAPP_REG_U8_SA_STROBE_TRIGGER SMIAPP_REG_MK_U8(0x0c24) +#define SMIAPP_REG_U8_SPECIAL_ACTUATOR_STATUS SMIAPP_REG_MK_U8(0x0c25) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_RS_CTRL SMIAPP_REG_MK_U16(0x0c26) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_RS_CTRL SMIAPP_REG_MK_U16(0x0c28) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_RS_CTRL SMIAPP_REG_MK_U8(0x0c2a) +#define SMIAPP_REG_U8_TFLASH_STROBE_COUNT_CTRL SMIAPP_REG_MK_U8(0x0c2b) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH2_HIGH_CTRL SMIAPP_REG_MK_U16(0x0c2c) +#define SMIAPP_REG_U16_TFLASH_STROBE_WIDTH_LOW_CTRL SMIAPP_REG_MK_U16(0x0c2e) +#define SMIAPP_REG_U8_LOW_LEVEL_CTRL SMIAPP_REG_MK_U8(0x0c80) +#define SMIAPP_REG_U16_MAIN_TRIGGER_REF_POINT SMIAPP_REG_MK_U16(0x0c82) +#define SMIAPP_REG_U16_MAIN_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c84) +#define SMIAPP_REG_U8_MAIN_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c86) +#define SMIAPP_REG_U16_PHASE1_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c88) +#define SMIAPP_REG_U8_PHASE1_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8a) +#define SMIAPP_REG_U16_PHASE2_TRIGGER_T3 SMIAPP_REG_MK_U16(0x0c8c) +#define SMIAPP_REG_U8_PHASE2_TRIGGER_COUNT SMIAPP_REG_MK_U8(0x0c8e) +#define SMIAPP_REG_U8_MECH_SHUTTER_CTRL SMIAPP_REG_MK_U8(0x0d00) +#define SMIAPP_REG_U8_OPERATION_MODE SMIAPP_REG_MK_U8(0x0d01) +#define SMIAPP_REG_U8_ACT_STATE1 SMIAPP_REG_MK_U8(0x0d02) +#define SMIAPP_REG_U8_ACT_STATE2 SMIAPP_REG_MK_U8(0x0d03) +#define SMIAPP_REG_U16_FOCUS_CHANGE SMIAPP_REG_MK_U16(0x0d80) +#define SMIAPP_REG_U16_FOCUS_CHANGE_CONTROL SMIAPP_REG_MK_U16(0x0d82) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE1 SMIAPP_REG_MK_U16(0x0d84) +#define SMIAPP_REG_U16_FOCUS_CHANGE_NUMBER_PHASE2 SMIAPP_REG_MK_U16(0x0d86) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE1 SMIAPP_REG_MK_U8(0x0d88) +#define SMIAPP_REG_U8_STROBE_COUNT_PHASE2 SMIAPP_REG_MK_U8(0x0d89) +#define SMIAPP_REG_U8_POSITION SMIAPP_REG_MK_U8(0x0d8a) +#define SMIAPP_REG_U8_BRACKETING_LUT_CONTROL SMIAPP_REG_MK_U8(0x0e00) +#define SMIAPP_REG_U8_BRACKETING_LUT_MODE SMIAPP_REG_MK_U8(0x0e01) +#define SMIAPP_REG_U8_BRACKETING_LUT_ENTRY_CONTROL SMIAPP_REG_MK_U8(0x0e02) +#define SMIAPP_REG_U8_LUT_PARAMETERS_START SMIAPP_REG_MK_U8(0x0e10) +#define SMIAPP_REG_U8_LUT_PARAMETERS_END SMIAPP_REG_MK_U8(0x0eff) +#define SMIAPP_REG_U16_INTEGRATION_TIME_CAPABILITY SMIAPP_REG_MK_U16(0x1000) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1004) +#define SMIAPP_REG_U16_COARSE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x1006) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN SMIAPP_REG_MK_U16(0x1008) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN SMIAPP_REG_MK_U16(0x100a) +#define SMIAPP_REG_U16_DIGITAL_GAIN_CAPABILITY SMIAPP_REG_MK_U16(0x1080) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MIN SMIAPP_REG_MK_U16(0x1084) +#define SMIAPP_REG_U16_DIGITAL_GAIN_MAX SMIAPP_REG_MK_U16(0x1086) +#define SMIAPP_REG_U16_DIGITAL_GAIN_STEP_SIZE SMIAPP_REG_MK_U16(0x1088) +#define SMIAPP_REG_F32_MIN_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1100) +#define SMIAPP_REG_F32_MAX_EXT_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1104) +#define SMIAPP_REG_U16_MIN_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x1108) +#define SMIAPP_REG_U16_MAX_PRE_PLL_CLK_DIV SMIAPP_REG_MK_U16(0x110a) +#define SMIAPP_REG_F32_MIN_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x110c) +#define SMIAPP_REG_F32_MAX_PLL_IP_FREQ_HZ SMIAPP_REG_MK_F32(0x1110) +#define SMIAPP_REG_U16_MIN_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1114) +#define SMIAPP_REG_U16_MAX_PLL_MULTIPLIER SMIAPP_REG_MK_U16(0x1116) +#define SMIAPP_REG_F32_MIN_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x1118) +#define SMIAPP_REG_F32_MAX_PLL_OP_FREQ_HZ SMIAPP_REG_MK_F32(0x111c) +#define SMIAPP_REG_U16_MIN_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1120) +#define SMIAPP_REG_U16_MAX_VT_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1122) +#define SMIAPP_REG_F32_MIN_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1124) +#define SMIAPP_REG_F32_MAX_VT_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1128) +#define SMIAPP_REG_F32_MIN_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x112c) +#define SMIAPP_REG_F32_MAX_VT_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1130) +#define SMIAPP_REG_U16_MIN_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1134) +#define SMIAPP_REG_U16_MAX_VT_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x1136) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1140) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES SMIAPP_REG_MK_U16(0x1142) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1144) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK SMIAPP_REG_MK_U16(0x1146) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK SMIAPP_REG_MK_U16(0x1148) +#define SMIAPP_REG_U16_MIN_FRAME_BLANKING_LINES SMIAPP_REG_MK_U16(0x114a) +#define SMIAPP_REG_U8_MIN_LINE_LENGTH_PCK_STEP_SIZE SMIAPP_REG_MK_U8(0x114c) +#define SMIAPP_REG_U16_MIN_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1160) +#define SMIAPP_REG_U16_MAX_OP_SYS_CLK_DIV SMIAPP_REG_MK_U16(0x1162) +#define SMIAPP_REG_F32_MIN_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1164) +#define SMIAPP_REG_F32_MAX_OP_SYS_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1168) +#define SMIAPP_REG_U16_MIN_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116c) +#define SMIAPP_REG_U16_MAX_OP_PIX_CLK_DIV SMIAPP_REG_MK_U16(0x116e) +#define SMIAPP_REG_F32_MIN_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1170) +#define SMIAPP_REG_F32_MAX_OP_PIX_CLK_FREQ_HZ SMIAPP_REG_MK_F32(0x1174) +#define SMIAPP_REG_U16_X_ADDR_MIN SMIAPP_REG_MK_U16(0x1180) +#define SMIAPP_REG_U16_Y_ADDR_MIN SMIAPP_REG_MK_U16(0x1182) +#define SMIAPP_REG_U16_X_ADDR_MAX SMIAPP_REG_MK_U16(0x1184) +#define SMIAPP_REG_U16_Y_ADDR_MAX SMIAPP_REG_MK_U16(0x1186) +#define SMIAPP_REG_U16_MIN_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x1188) +#define SMIAPP_REG_U16_MIN_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118a) +#define SMIAPP_REG_U16_MAX_X_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118c) +#define SMIAPP_REG_U16_MAX_Y_OUTPUT_SIZE SMIAPP_REG_MK_U16(0x118e) +#define SMIAPP_REG_U16_MIN_EVEN_INC SMIAPP_REG_MK_U16(0x11c0) +#define SMIAPP_REG_U16_MAX_EVEN_INC SMIAPP_REG_MK_U16(0x11c2) +#define SMIAPP_REG_U16_MIN_ODD_INC SMIAPP_REG_MK_U16(0x11c4) +#define SMIAPP_REG_U16_MAX_ODD_INC SMIAPP_REG_MK_U16(0x11c6) +#define SMIAPP_REG_U16_SCALING_CAPABILITY SMIAPP_REG_MK_U16(0x1200) +#define SMIAPP_REG_U16_SCALER_M_MIN SMIAPP_REG_MK_U16(0x1204) +#define SMIAPP_REG_U16_SCALER_M_MAX SMIAPP_REG_MK_U16(0x1206) +#define SMIAPP_REG_U16_SCALER_N_MIN SMIAPP_REG_MK_U16(0x1208) +#define SMIAPP_REG_U16_SCALER_N_MAX SMIAPP_REG_MK_U16(0x120a) +#define SMIAPP_REG_U16_SPATIAL_SAMPLING_CAPABILITY SMIAPP_REG_MK_U16(0x120c) +#define SMIAPP_REG_U8_DIGITAL_CROP_CAPABILITY SMIAPP_REG_MK_U8(0x120e) +#define SMIAPP_REG_U16_COMPRESSION_CAPABILITY SMIAPP_REG_MK_U16(0x1300) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINRED SMIAPP_REG_MK_U16(0x1400) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINRED SMIAPP_REG_MK_U16(0x1402) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINRED SMIAPP_REG_MK_U16(0x1404) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINGREEN SMIAPP_REG_MK_U16(0x1406) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINGREEN SMIAPP_REG_MK_U16(0x1408) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINGREEN SMIAPP_REG_MK_U16(0x140a) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_REDINBLUE SMIAPP_REG_MK_U16(0x140c) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_GREENINBLUE SMIAPP_REG_MK_U16(0x140e) +#define SMIAPP_REG_U16_MATRIX_ELEMENT_BLUEINBLUE SMIAPP_REG_MK_U16(0x1410) +#define SMIAPP_REG_U16_FIFO_SIZE_PIXELS SMIAPP_REG_MK_U16(0x1500) +#define SMIAPP_REG_U8_FIFO_SUPPORT_CAPABILITY SMIAPP_REG_MK_U8(0x1502) +#define SMIAPP_REG_U8_DPHY_CTRL_CAPABILITY SMIAPP_REG_MK_U8(0x1600) +#define SMIAPP_REG_U8_CSI_LANE_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1601) +#define SMIAPP_REG_U8_CSI_SIGNALLING_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1602) +#define SMIAPP_REG_U8_FAST_STANDBY_CAPABILITY SMIAPP_REG_MK_U8(0x1603) +#define SMIAPP_REG_U8_CCI_ADDRESS_CONTROL_CAPABILITY SMIAPP_REG_MK_U8(0x1604) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_1_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1608) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_2_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x160c) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_3_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1610) +#define SMIAPP_REG_U32_MAX_PER_LANE_BITRATE_4_LANE_MODE_MBPS SMIAPP_REG_MK_U32(0x1614) +#define SMIAPP_REG_U8_TEMP_SENSOR_CAPABILITY SMIAPP_REG_MK_U8(0x1618) +#define SMIAPP_REG_U16_MIN_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1700) +#define SMIAPP_REG_U16_MAX_FRAME_LENGTH_LINES_BIN SMIAPP_REG_MK_U16(0x1702) +#define SMIAPP_REG_U16_MIN_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1704) +#define SMIAPP_REG_U16_MAX_LINE_LENGTH_PCK_BIN SMIAPP_REG_MK_U16(0x1706) +#define SMIAPP_REG_U16_MIN_LINE_BLANKING_PCK_BIN SMIAPP_REG_MK_U16(0x1708) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MIN_BIN SMIAPP_REG_MK_U16(0x170a) +#define SMIAPP_REG_U16_FINE_INTEGRATION_TIME_MAX_MARGIN_BIN SMIAPP_REG_MK_U16(0x170c) +#define SMIAPP_REG_U8_BINNING_CAPABILITY SMIAPP_REG_MK_U8(0x1710) +#define SMIAPP_REG_U8_BINNING_WEIGHTING_CAPABILITY SMIAPP_REG_MK_U8(0x1711) +#define SMIAPP_REG_U8_BINNING_SUBTYPES SMIAPP_REG_MK_U8(0x1712) +#define SMIAPP_REG_U8_BINNING_TYPE_n(n) SMIAPP_REG_MK_U8(0x1713 + (n)) /* 1 <= n <= 237 */ +#define SMIAPP_REG_U8_DATA_TRANSFER_IF_CAPABILITY SMIAPP_REG_MK_U8(0x1800) +#define SMIAPP_REG_U8_SHADING_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1900) +#define SMIAPP_REG_U8_GREEN_IMBALANCE_CAPABILITY SMIAPP_REG_MK_U8(0x1901) +#define SMIAPP_REG_U8_BLACK_LEVEL_CAPABILITY SMIAPP_REG_MK_U8(0x1902) +#define SMIAPP_REG_U8_MODULE_SPECIFIC_CORRECTION_CAPABILITY SMIAPP_REG_MK_U8(0x1903) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY SMIAPP_REG_MK_U16(0x1904) +#define SMIAPP_REG_U16_DEFECT_CORRECTION_CAPABILITY_2 SMIAPP_REG_MK_U16(0x1906) +#define SMIAPP_REG_U8_EDOF_CAPABILITY SMIAPP_REG_MK_U8(0x1980) +#define SMIAPP_REG_U8_ESTIMATION_FRAMES SMIAPP_REG_MK_U8(0x1981) +#define SMIAPP_REG_U8_SUPPORTS_SHARPNESS_ADJ SMIAPP_REG_MK_U8(0x1982) +#define SMIAPP_REG_U8_SUPPORTS_DENOISING_ADJ SMIAPP_REG_MK_U8(0x1983) +#define SMIAPP_REG_U8_SUPPORTS_MODULE_SPECIFIC_ADJ SMIAPP_REG_MK_U8(0x1984) +#define SMIAPP_REG_U8_SUPPORTS_DEPTH_OF_FIELD_ADJ SMIAPP_REG_MK_U8(0x1985) +#define SMIAPP_REG_U8_SUPPORTS_FOCUS_DISTANCE_ADJ SMIAPP_REG_MK_U8(0x1986) +#define SMIAPP_REG_U8_COLOUR_FEEDBACK_CAPABILITY SMIAPP_REG_MK_U8(0x1987) +#define SMIAPP_REG_U8_EDOF_SUPPORT_AB_NXM SMIAPP_REG_MK_U8(0x1988) +#define SMIAPP_REG_U8_ESTIMATION_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x19c0) +#define SMIAPP_REG_U8_ESTIMATION_ZONE_CAPABILITY SMIAPP_REG_MK_U8(0x19c1) +#define SMIAPP_REG_U16_EST_DEPTH_OF_FIELD SMIAPP_REG_MK_U16(0x19c2) +#define SMIAPP_REG_U16_EST_FOCUS_DISTANCE SMIAPP_REG_MK_U16(0x19c4) +#define SMIAPP_REG_U16_CAPABILITY_TRDY_MIN SMIAPP_REG_MK_U16(0x1a00) +#define SMIAPP_REG_U8_FLASH_MODE_CAPABILITY SMIAPP_REG_MK_U8(0x1a02) +#define SMIAPP_REG_U16_MECH_SHUT_AND_ACT_START_ADDR SMIAPP_REG_MK_U16(0x1b02) +#define SMIAPP_REG_U8_ACTUATOR_CAPABILITY SMIAPP_REG_MK_U8(0x1b04) +#define SMIAPP_REG_U16_ACTUATOR_TYPE SMIAPP_REG_MK_U16(0x1b40) +#define SMIAPP_REG_U8_AF_DEVICE_ADDRESS SMIAPP_REG_MK_U8(0x1b42) +#define SMIAPP_REG_U16_FOCUS_CHANGE_ADDRESS SMIAPP_REG_MK_U16(0x1b44) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_1 SMIAPP_REG_MK_U8(0x1c00) +#define SMIAPP_REG_U8_BRACKETING_LUT_CAPABILITY_2 SMIAPP_REG_MK_U8(0x1c01) +#define SMIAPP_REG_U8_BRACKETING_LUT_SIZE SMIAPP_REG_MK_U8(0x1c02) diff --git a/drivers/media/video/smiapp/smiapp-reg.h b/drivers/media/video/smiapp/smiapp-reg.h new file mode 100644 index 000000000000..d0167aa17534 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-reg.h @@ -0,0 +1,122 @@ +/* + * drivers/media/video/smiapp/smiapp-reg.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_REG_H_ +#define __SMIAPP_REG_H_ + +#include "smiapp-reg-defs.h" + +/* Bits for above register */ +#define SMIAPP_IMAGE_ORIENTATION_HFLIP (1 << 0) +#define SMIAPP_IMAGE_ORIENTATION_VFLIP (1 << 1) + +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_EN (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_RD_EN (0 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_WR_EN (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_CTRL_ERR_CLEAR (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_RD_READY (1 << 0) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_WR_READY (1 << 1) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EDATA (1 << 2) +#define SMIAPP_DATA_TRANSFER_IF_1_STATUS_EUSAGE (1 << 3) + +#define SMIAPP_SOFTWARE_RESET (1 << 0) + +#define SMIAPP_FLASH_MODE_CAPABILITY_SINGLE_STROBE (1 << 0) +#define SMIAPP_FLASH_MODE_CAPABILITY_MULTIPLE_STROBE (1 << 1) + +#define SMIAPP_DPHY_CTRL_AUTOMATIC 0 +/* DPHY control based on REQUESTED_LINK_BIT_RATE_MBPS */ +#define SMIAPP_DPHY_CTRL_UI 1 +#define SMIAPP_DPHY_CTRL_REGISTER 2 + +#define SMIAPP_COMPRESSION_MODE_SIMPLE_PREDICTOR 1 +#define SMIAPP_COMPRESSION_MODE_ADVANCED_PREDICTOR 2 + +#define SMIAPP_MODE_SELECT_SOFTWARE_STANDBY 0 +#define SMIAPP_MODE_SELECT_STREAMING 1 + +#define SMIAPP_SCALING_MODE_NONE 0 +#define SMIAPP_SCALING_MODE_HORIZONTAL 1 +#define SMIAPP_SCALING_MODE_BOTH 2 + +#define SMIAPP_SCALING_CAPABILITY_NONE 0 +#define SMIAPP_SCALING_CAPABILITY_HORIZONTAL 1 +#define SMIAPP_SCALING_CAPABILITY_BOTH 2 /* horizontal/both */ + +/* digital crop right before scaler */ +#define SMIAPP_DIGITAL_CROP_CAPABILITY_NONE 0 +#define SMIAPP_DIGITAL_CROP_CAPABILITY_INPUT_CROP 1 + +#define SMIAPP_BINNING_CAPABILITY_NO 0 +#define SMIAPP_BINNING_CAPABILITY_YES 1 + +/* Maximum number of binning subtypes */ +#define SMIAPP_BINNING_SUBTYPES 253 + +#define SMIAPP_PIXEL_ORDER_GRBG 0 +#define SMIAPP_PIXEL_ORDER_RGGB 1 +#define SMIAPP_PIXEL_ORDER_BGGR 2 +#define SMIAPP_PIXEL_ORDER_GBRG 3 + +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL 1 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED 2 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_NORMAL_N 8 +#define SMIAPP_DATA_FORMAT_MODEL_TYPE_EXTENDED_N 16 + +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_2BYTE 0x01 +#define SMIAPP_FRAME_FORMAT_MODEL_TYPE_4BYTE 0x02 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NROWS_MASK 0x0f +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_MASK 0xf0 +#define SMIAPP_FRAME_FORMAT_MODEL_SUBTYPE_NCOLS_SHIFT 4 + +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_MASK 0xf000 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELCODE_SHIFT 12 +#define SMIAPP_FRAME_FORMAT_DESC_2_PIXELS_MASK 0x0fff + +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_MASK 0xf0000000 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELCODE_SHIFT 28 +#define SMIAPP_FRAME_FORMAT_DESC_4_PIXELS_MASK 0x0000ffff + +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_EMBEDDED 1 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DUMMY 2 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_BLACK 3 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_DARK 4 +#define SMIAPP_FRAME_FORMAT_DESC_PIXELCODE_VISIBLE 5 + +#define SMIAPP_FAST_STANDBY_CTRL_COMPLETE_FRAMES 0 +#define SMIAPP_FAST_STANDBY_CTRL_IMMEDIATE 1 + +/* Scaling N factor */ +#define SMIAPP_SCALE_N 16 + +/* Image statistics registers */ +/* Registers 0x2000 to 0x2fff are reserved for future + * use for statistics features. + */ + +/* Manufacturer Specific Registers: 0x3000 to 0x3fff + * The manufacturer specifies these as a black box. + */ + +#endif /* __SMIAPP_REG_H_ */ diff --git a/drivers/media/video/smiapp/smiapp-regs.c b/drivers/media/video/smiapp/smiapp-regs.c new file mode 100644 index 000000000000..b1812b17a407 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-regs.c @@ -0,0 +1,273 @@ +/* + * drivers/media/video/smiapp/smiapp-regs.c + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#include +#include + +#include "smiapp.h" +#include "smiapp-regs.h" + +static uint32_t float_to_u32_mul_1000000(struct i2c_client *client, + uint32_t phloat) +{ + int32_t exp; + uint64_t man; + + if (phloat >= 0x80000000) { + dev_err(&client->dev, "this is a negative number\n"); + return 0; + } + + if (phloat == 0x7f800000) + return ~0; /* Inf. */ + + if ((phloat & 0x7f800000) == 0x7f800000) { + dev_err(&client->dev, "NaN or other special number\n"); + return 0; + } + + /* Valid cases begin here */ + if (phloat == 0) + return 0; /* Valid zero */ + + if (phloat > 0x4f800000) + return ~0; /* larger than 4294967295 */ + + /* + * Unbias exponent (note how phloat is now guaranteed to + * have 0 in the high bit) + */ + exp = ((int32_t)phloat >> 23) - 127; + + /* Extract mantissa, add missing '1' bit and it's in MHz */ + man = ((phloat & 0x7fffff) | 0x800000) * 1000000ULL; + + if (exp < 0) + man >>= -exp; + else + man <<= exp; + + man >>= 23; /* Remove mantissa bias */ + + return man & 0xffffffff; +} + + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int ____smiapp_read(struct smiapp_sensor *sensor, u16 reg, + u16 len, u32 *val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data[4]; + u16 offset = reg; + int r; + + msg.addr = client->addr; + msg.flags = 0; + msg.len = 2; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (offset >> 8); + data[1] = (u8) offset; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + msg.len = len; + msg.flags = I2C_M_RD; + r = i2c_transfer(client->adapter, &msg, 1); + if (r != 1) { + if (r >= 0) + r = -EBUSY; + goto err; + } + + *val = 0; + /* high byte comes first */ + switch (len) { + case SMIA_REG_32BIT: + *val = (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + + data[3]; + break; + case SMIA_REG_16BIT: + *val = (data[0] << 8) + data[1]; + break; + case SMIA_REG_8BIT: + *val = data[0]; + break; + default: + BUG(); + } + + return 0; + +err: + dev_err(&client->dev, "read from offset 0x%x error %d\n", offset, r); + + return r; +} + +/* Read a register using 8-bit access only. */ +static int ____smiapp_read_8only(struct smiapp_sensor *sensor, u16 reg, + u16 len, u32 *val) +{ + unsigned int i; + int rval; + + *val = 0; + + for (i = 0; i < len; i++) { + u32 val8; + + rval = ____smiapp_read(sensor, reg + i, 1, &val8); + if (rval < 0) + return rval; + *val |= val8 << ((len - i - 1) << 3); + } + + return 0; +} + +/* + * Read a 8/16/32-bit i2c register. The value is returned in 'val'. + * Returns zero if successful, or non-zero otherwise. + */ +static int __smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val, + bool only8) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + unsigned int len = (u8)(reg >> 16); + int rval; + + if (len != SMIA_REG_8BIT && len != SMIA_REG_16BIT + && len != SMIA_REG_32BIT) + return -EINVAL; + + if (smiapp_quirk_reg(sensor, reg, val)) + goto found_quirk; + + if (len == SMIA_REG_8BIT && !only8) + rval = ____smiapp_read(sensor, (u16)reg, len, val); + else + rval = ____smiapp_read_8only(sensor, (u16)reg, len, val); + if (rval < 0) + return rval; + +found_quirk: + if (reg & SMIA_REG_FLAG_FLOAT) + *val = float_to_u32_mul_1000000(client, *val); + + return 0; +} + +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return __smiapp_read( + sensor, reg, val, + smiapp_needs_quirk(sensor, + SMIAPP_QUIRK_FLAG_8BIT_READ_ONLY)); +} + +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val) +{ + return __smiapp_read(sensor, reg, val, true); +} + +/* + * Write to a 8/16-bit register. + * Returns zero if successful, or non-zero otherwise. + */ +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val) +{ + struct i2c_client *client = v4l2_get_subdevdata(&sensor->src->sd); + struct i2c_msg msg; + unsigned char data[6]; + unsigned int retries; + unsigned int flags = reg >> 24; + unsigned int len = (u8)(reg >> 16); + u16 offset = reg; + int r; + + if ((len != SMIA_REG_8BIT && len != SMIA_REG_16BIT && + len != SMIA_REG_32BIT) || flags) + return -EINVAL; + + msg.addr = client->addr; + msg.flags = 0; /* Write */ + msg.len = 2 + len; + msg.buf = data; + + /* high byte goes out first */ + data[0] = (u8) (reg >> 8); + data[1] = (u8) (reg & 0xff); + + switch (len) { + case SMIA_REG_8BIT: + data[2] = val; + break; + case SMIA_REG_16BIT: + data[2] = val >> 8; + data[3] = val; + break; + case SMIA_REG_32BIT: + data[2] = val >> 24; + data[3] = val >> 16; + data[4] = val >> 8; + data[5] = val; + break; + default: + BUG(); + } + + for (retries = 0; retries < 5; retries++) { + /* + * Due to unknown reason sensor stops responding. This + * loop is a temporaty solution until the root cause + * is found. + */ + r = i2c_transfer(client->adapter, &msg, 1); + if (r == 1) { + if (retries) + dev_err(&client->dev, + "sensor i2c stall encountered. " + "retries: %d\n", retries); + return 0; + } + + usleep_range(2000, 2000); + } + + dev_err(&client->dev, + "wrote 0x%x to offset 0x%x error %d\n", val, offset, r); + + return r; +} diff --git a/drivers/media/video/smiapp/smiapp-regs.h b/drivers/media/video/smiapp/smiapp-regs.h new file mode 100644 index 000000000000..7f9013b47971 --- /dev/null +++ b/drivers/media/video/smiapp/smiapp-regs.h @@ -0,0 +1,49 @@ +/* + * include/media/smiapp/smiapp-regs.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef SMIAPP_REGS_H +#define SMIAPP_REGS_H + +#include +#include + +/* Use upper 8 bits of the type field for flags */ +#define SMIA_REG_FLAG_FLOAT (1 << 24) + +#define SMIA_REG_8BIT 1 +#define SMIA_REG_16BIT 2 +#define SMIA_REG_32BIT 4 +struct smia_reg { + u16 type; + u16 reg; /* 16-bit offset */ + u32 val; /* 8/16/32-bit value */ +}; + +struct smiapp_sensor; + +int smiapp_read(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_read_8only(struct smiapp_sensor *sensor, u32 reg, u32 *val); +int smiapp_write(struct smiapp_sensor *sensor, u32 reg, u32 val); + +#endif diff --git a/drivers/media/video/smiapp/smiapp.h b/drivers/media/video/smiapp/smiapp.h new file mode 100644 index 000000000000..587f7f11238d --- /dev/null +++ b/drivers/media/video/smiapp/smiapp.h @@ -0,0 +1,252 @@ +/* + * drivers/media/video/smiapp/smiapp.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2010--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_PRIV_H_ +#define __SMIAPP_PRIV_H_ + +#include +#include +#include +#include + +#include "smiapp-pll.h" +#include "smiapp-reg.h" +#include "smiapp-regs.h" +#include "smiapp-quirk.h" + +/* + * Standard SMIA++ constants + */ +#define SMIA_VERSION_1 10 +#define SMIAPP_VERSION_0_8 8 /* Draft 0.8 */ +#define SMIAPP_VERSION_0_9 9 /* Draft 0.9 */ +#define SMIAPP_VERSION_1 10 + +#define SMIAPP_PROFILE_0 0 +#define SMIAPP_PROFILE_1 1 +#define SMIAPP_PROFILE_2 2 + +#define SMIAPP_NVM_PAGE_SIZE 64 /* bytes */ + +#define SMIAPP_RESET_DELAY_CLOCKS 2400 +#define SMIAPP_RESET_DELAY(clk) \ + (1000 + (SMIAPP_RESET_DELAY_CLOCKS * 1000 \ + + (clk) / 1000 - 1) / ((clk) / 1000)) + +#include "smiapp-limits.h" + +struct smiapp_quirk; + +#define SMIAPP_MODULE_IDENT_FLAG_REV_LE (1 << 0) + +struct smiapp_module_ident { + u8 manufacturer_id; + u16 model_id; + u8 revision_number_major; + + u8 flags; + + char *name; + const struct smiapp_quirk *quirk; +}; + +struct smiapp_module_info { + u32 manufacturer_id; + u32 model_id; + u32 revision_number_major; + u32 revision_number_minor; + + u32 module_year; + u32 module_month; + u32 module_day; + + u32 sensor_manufacturer_id; + u32 sensor_model_id; + u32 sensor_revision_number; + u32 sensor_firmware_version; + + u32 smia_version; + u32 smiapp_version; + + u32 smiapp_profile; + + char *name; + const struct smiapp_quirk *quirk; +}; + +#define SMIAPP_IDENT_FQ(manufacturer, model, rev, fl, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = fl, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_LQ(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT_L(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = SMIAPP_MODULE_IDENT_FLAG_REV_LE, \ + .name = _name, } + +#define SMIAPP_IDENT_Q(manufacturer, model, rev, _name, _quirk) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, \ + .quirk = _quirk, } + +#define SMIAPP_IDENT(manufacturer, model, rev, _name) \ + { .manufacturer_id = manufacturer, \ + .model_id = model, \ + .revision_number_major = rev, \ + .flags = 0, \ + .name = _name, } + +struct smiapp_reg_limits { + u32 addr; + char *what; +}; + +extern struct smiapp_reg_limits smiapp_reg_limits[]; + +struct smiapp_csi_data_format { + u32 code; + u8 width; + u8 compressed; + u8 pixel_order; +}; + +#define SMIAPP_SUBDEVS 3 + +#define SMIAPP_PA_PAD_SRC 0 +#define SMIAPP_PAD_SINK 0 +#define SMIAPP_PAD_SRC 1 +#define SMIAPP_PADS 2 + +struct smiapp_binning_subtype { + u8 horizontal:4; + u8 vertical:4; +} __packed; + +struct smiapp_subdev { + struct v4l2_subdev sd; + struct media_pad pads[2]; + struct v4l2_rect sink_fmt; + struct v4l2_rect crop[2]; + struct v4l2_rect compose; /* compose on sink */ + unsigned short sink_pad; + unsigned short source_pad; + int npads; + struct smiapp_sensor *sensor; + struct v4l2_ctrl_handler ctrl_handler; +}; + +/* + * struct smiapp_sensor - Main device structure + */ +struct smiapp_sensor { + /* + * "mutex" is used to serialise access to all fields here + * except v4l2_ctrls at the end of the struct. "mutex" is also + * used to serialise access to file handle specific + * information. The exception to this rule is the power_mutex + * below. + */ + struct mutex mutex; + /* + * power_mutex is used to serialise power management related + * activities. Acquiring "mutex" at that time isn't necessary + * since there are no other users anyway. + */ + struct mutex power_mutex; + struct smiapp_subdev ssds[SMIAPP_SUBDEVS]; + u32 ssds_used; + struct smiapp_subdev *src; + struct smiapp_subdev *binner; + struct smiapp_subdev *scaler; + struct smiapp_subdev *pixel_array; + struct smiapp_platform_data *platform_data; + struct regulator *vana; + struct clk *ext_clk; + u32 limits[SMIAPP_LIMIT_LAST]; + u8 nbinning_subtypes; + struct smiapp_binning_subtype binning_subtypes[SMIAPP_BINNING_SUBTYPES]; + u32 mbus_frame_fmts; + const struct smiapp_csi_data_format *csi_format; + const struct smiapp_csi_data_format *internal_csi_format; + u32 default_mbus_frame_fmts; + int default_pixel_order; + + u8 binning_horizontal; + u8 binning_vertical; + + u8 scale_m; + u8 scaling_mode; + + u8 hvflip_inv_mask; /* H/VFLIP inversion due to sensor orientation */ + u8 flash_capability; + u8 frame_skip; + + int power_count; + + bool streaming; + bool dev_init_done; + + u8 *nvm; /* nvm memory buffer */ + unsigned int nvm_size; /* bytes */ + + struct smiapp_module_info minfo; + + struct smiapp_pll pll; + + /* Pixel array controls */ + struct v4l2_ctrl *analog_gain; + struct v4l2_ctrl *exposure; + struct v4l2_ctrl *hflip; + struct v4l2_ctrl *vflip; + struct v4l2_ctrl *vblank; + struct v4l2_ctrl *hblank; + struct v4l2_ctrl *pixel_rate_parray; + /* src controls */ + struct v4l2_ctrl *link_freq; + struct v4l2_ctrl *pixel_rate_csi; +}; + +#define to_smiapp_subdev(_sd) \ + container_of(_sd, struct smiapp_subdev, sd) + +#define to_smiapp_sensor(_sd) \ + (to_smiapp_subdev(_sd)->sensor) + +#endif /* __SMIAPP_PRIV_H_ */ diff --git a/drivers/media/video/sn9c102/sn9c102_core.c b/drivers/media/video/sn9c102/sn9c102_core.c index c2882fa5be85..19ea780b16ff 100644 --- a/drivers/media/video/sn9c102/sn9c102_core.c +++ b/drivers/media/video/sn9c102/sn9c102_core.c @@ -995,10 +995,8 @@ static int sn9c102_stop_transfer(struct sn9c102_device* cam) static int sn9c102_stream_interrupt(struct sn9c102_device* cam) { - long timeout; - cam->stream = STREAM_INTERRUPT; - timeout = wait_event_timeout(cam->wait_stream, + wait_event_timeout(cam->wait_stream, (cam->stream == STREAM_OFF) || (cam->state & DEV_DISCONNECTED), SN9C102_URB_TIMEOUT); diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c index aedb970d13f6..0421bf9453b4 100644 --- a/drivers/media/video/soc_camera.c +++ b/drivers/media/video/soc_camera.c @@ -164,35 +164,38 @@ static int soc_camera_try_fmt(struct soc_camera_device *icd, struct v4l2_format *f) { struct soc_camera_host *ici = to_soc_camera_host(icd->parent); + const struct soc_camera_format_xlate *xlate; struct v4l2_pix_format *pix = &f->fmt.pix; int ret; dev_dbg(icd->pdev, "TRY_FMT(%c%c%c%c, %ux%u)\n", pixfmtstr(pix->pixelformat), pix->width, pix->height); - pix->bytesperline = 0; - pix->sizeimage = 0; + if (!(ici->capabilities & SOCAM_HOST_CAP_STRIDE)) { + pix->bytesperline = 0; + pix->sizeimage = 0; + } ret = ici->ops->try_fmt(icd, f); if (ret < 0) return ret; - if (!pix->sizeimage) { - if (!pix->bytesperline) { - const struct soc_camera_format_xlate *xlate; + xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); + if (!xlate) + return -EINVAL; - xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); - if (!xlate) - return -EINVAL; + ret = soc_mbus_bytes_per_line(pix->width, xlate->host_fmt); + if (ret < 0) + return ret; - ret = soc_mbus_bytes_per_line(pix->width, - xlate->host_fmt); - if (ret > 0) - pix->bytesperline = ret; - } - if (pix->bytesperline) - pix->sizeimage = pix->bytesperline * pix->height; - } + pix->bytesperline = max_t(u32, pix->bytesperline, ret); + + ret = soc_mbus_image_size(xlate->host_fmt, pix->bytesperline, + pix->height); + if (ret < 0) + return ret; + + pix->sizeimage = max_t(u32, pix->sizeimage, ret); return 0; } @@ -257,13 +260,13 @@ static int soc_camera_g_std(struct file *file, void *priv, v4l2_std_id *a) return v4l2_subdev_call(sd, core, g_std, a); } -static int soc_camera_enum_fsizes(struct file *file, void *fh, +static int soc_camera_enum_framesizes(struct file *file, void *fh, struct v4l2_frmsizeenum *fsize) { struct soc_camera_device *icd = file->private_data; struct soc_camera_host *ici = to_soc_camera_host(icd->parent); - return ici->ops->enum_fsizes(icd, fsize); + return ici->ops->enum_framesizes(icd, fsize); } static int soc_camera_reqbufs(struct file *file, void *priv, @@ -1244,8 +1247,8 @@ static int default_s_parm(struct soc_camera_device *icd, return v4l2_subdev_call(sd, video, s_parm, parm); } -static int default_enum_fsizes(struct soc_camera_device *icd, - struct v4l2_frmsizeenum *fsize) +static int default_enum_framesizes(struct soc_camera_device *icd, + struct v4l2_frmsizeenum *fsize) { int ret; struct v4l2_subdev *sd = soc_camera_to_subdev(icd); @@ -1259,7 +1262,7 @@ static int default_enum_fsizes(struct soc_camera_device *icd, /* map xlate-code to pixel_format, sensor only handle xlate-code*/ fsize_mbus.pixel_format = xlate->code; - ret = v4l2_subdev_call(sd, video, enum_mbus_fsizes, &fsize_mbus); + ret = v4l2_subdev_call(sd, video, enum_framesizes, &fsize_mbus); if (ret < 0) return ret; @@ -1298,8 +1301,8 @@ int soc_camera_host_register(struct soc_camera_host *ici) ici->ops->set_parm = default_s_parm; if (!ici->ops->get_parm) ici->ops->get_parm = default_g_parm; - if (!ici->ops->enum_fsizes) - ici->ops->enum_fsizes = default_enum_fsizes; + if (!ici->ops->enum_framesizes) + ici->ops->enum_framesizes = default_enum_framesizes; mutex_lock(&list_lock); list_for_each_entry(ix, &hosts, list) { @@ -1390,7 +1393,7 @@ static const struct v4l2_ioctl_ops soc_camera_ioctl_ops = { .vidioc_s_input = soc_camera_s_input, .vidioc_s_std = soc_camera_s_std, .vidioc_g_std = soc_camera_g_std, - .vidioc_enum_framesizes = soc_camera_enum_fsizes, + .vidioc_enum_framesizes = soc_camera_enum_framesizes, .vidioc_reqbufs = soc_camera_reqbufs, .vidioc_querybuf = soc_camera_querybuf, .vidioc_qbuf = soc_camera_qbuf, @@ -1429,6 +1432,10 @@ static int video_dev_create(struct soc_camera_device *icd) vdev->tvnorms = V4L2_STD_UNKNOWN; vdev->ctrl_handler = &icd->ctrl_handler; vdev->lock = &icd->video_lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); icd->vdev = vdev; diff --git a/drivers/media/video/soc_mediabus.c b/drivers/media/video/soc_mediabus.c index cf7f2194ded4..89dce097a827 100644 --- a/drivers/media/video/soc_mediabus.c +++ b/drivers/media/video/soc_mediabus.c @@ -24,6 +24,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YVYU8_2X8, @@ -33,6 +34,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_UYVY8_2X8, @@ -42,6 +44,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_VYUY8_2X8, @@ -51,6 +54,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_LE, @@ -60,6 +64,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB555_2X8_PADHI_BE, @@ -69,6 +74,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB565_2X8_LE, @@ -78,6 +84,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB565_2X8_BE, @@ -87,6 +94,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR8_1X8, @@ -96,6 +104,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_1X10, @@ -105,6 +114,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_Y8_1X8, @@ -114,6 +124,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_Y10_1X10, @@ -123,6 +134,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_LE, @@ -132,6 +144,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_LE, @@ -141,6 +154,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADLO, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADHI_BE, @@ -150,6 +164,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR10_2X8_PADLO_BE, @@ -159,6 +174,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADLO, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_JPEG_1X8, @@ -168,6 +184,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_VARIABLE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_RGB444_2X8_PADHI_BE, @@ -177,6 +194,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_2X8_PADHI, .order = SOC_MBUS_ORDER_BE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YUYV8_1_5X8, @@ -186,6 +204,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YVYU8_1_5X8, @@ -195,6 +214,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_1_5X8, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_UYVY8_1X16, @@ -204,6 +224,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 16, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_VYUY8_1X16, @@ -213,6 +234,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 16, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YUYV8_1X16, @@ -222,6 +244,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 16, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_YVYU8_1X16, @@ -231,6 +254,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 16, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGRBG8_1X8, @@ -240,6 +264,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGRBG10_DPCM8_1X8, @@ -249,6 +274,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 8, .packing = SOC_MBUS_PACKING_NONE, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGBRG10_1X10, @@ -258,6 +284,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGRBG10_1X10, @@ -267,6 +294,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SRGGB10_1X10, @@ -276,6 +304,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 10, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SBGGR12_1X12, @@ -285,6 +314,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 12, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGBRG12_1X12, @@ -294,6 +324,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 12, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SGRBG12_1X12, @@ -303,6 +334,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 12, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, { .code = V4L2_MBUS_FMT_SRGGB12_1X12, @@ -312,6 +344,7 @@ static const struct soc_mbus_lookup mbus_fmt[] = { .bits_per_sample = 12, .packing = SOC_MBUS_PACKING_EXTEND16, .order = SOC_MBUS_ORDER_LE, + .layout = SOC_MBUS_LAYOUT_PACKED, }, }, }; @@ -345,6 +378,9 @@ EXPORT_SYMBOL(soc_mbus_samples_per_pixel); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) { + if (mf->layout != SOC_MBUS_LAYOUT_PACKED) + return width * mf->bits_per_sample / 8; + switch (mf->packing) { case SOC_MBUS_PACKING_NONE: return width * mf->bits_per_sample / 8; @@ -361,6 +397,24 @@ s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf) } EXPORT_SYMBOL(soc_mbus_bytes_per_line); +s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, + u32 bytes_per_line, u32 height) +{ + if (mf->layout == SOC_MBUS_LAYOUT_PACKED) + return bytes_per_line * height; + + switch (mf->packing) { + case SOC_MBUS_PACKING_2X8_PADHI: + case SOC_MBUS_PACKING_2X8_PADLO: + return bytes_per_line * height * 2; + case SOC_MBUS_PACKING_1_5X8: + return bytes_per_line * height * 3 / 2; + default: + return -EINVAL; + } +} +EXPORT_SYMBOL(soc_mbus_image_size); + const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( enum v4l2_mbus_pixelcode code, const struct soc_mbus_lookup *lookup, diff --git a/drivers/media/video/sta2x11_vip.c b/drivers/media/video/sta2x11_vip.c new file mode 100644 index 000000000000..4c10205264d4 --- /dev/null +++ b/drivers/media/video/sta2x11_vip.c @@ -0,0 +1,1550 @@ +/* + * This is the driver for the STA2x11 Video Input Port. + * + * Copyright (C) 2010 WindRiver Systems, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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 St - Fifth Floor, Boston, MA 02110-1301 USA. + * + * The full GNU General Public License is included in this distribution in + * the file called "COPYING". + * + * Author: Andreas Kies + * Vlad Lungu + * + */ + +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sta2x11_vip.h" + +#define DRV_NAME "sta2x11_vip" +#define DRV_VERSION "1.3" + +#ifndef PCI_DEVICE_ID_STMICRO_VIP +#define PCI_DEVICE_ID_STMICRO_VIP 0xCC0D +#endif + +#define MAX_FRAMES 4 + +/*Register offsets*/ +#define DVP_CTL 0x00 +#define DVP_TFO 0x04 +#define DVP_TFS 0x08 +#define DVP_BFO 0x0C +#define DVP_BFS 0x10 +#define DVP_VTP 0x14 +#define DVP_VBP 0x18 +#define DVP_VMP 0x1C +#define DVP_ITM 0x98 +#define DVP_ITS 0x9C +#define DVP_STA 0xA0 +#define DVP_HLFLN 0xA8 +#define DVP_RGB 0xC0 +#define DVP_PKZ 0xF0 + +/*Register fields*/ +#define DVP_CTL_ENA 0x00000001 +#define DVP_CTL_RST 0x80000000 +#define DVP_CTL_DIS (~0x00040001) + +#define DVP_IT_VSB 0x00000008 +#define DVP_IT_VST 0x00000010 +#define DVP_IT_FIFO 0x00000020 + +#define DVP_HLFLN_SD 0x00000001 + +#define REG_WRITE(vip, reg, value) iowrite32((value), (vip->iomem)+(reg)) +#define REG_READ(vip, reg) ioread32((vip->iomem)+(reg)) + +#define SAVE_COUNT 8 +#define AUX_COUNT 3 +#define IRQ_COUNT 1 + +/** + * struct sta2x11_vip - All internal data for one instance of device + * @v4l2_dev: device registered in v4l layer + * @video_dev: properties of our device + * @pdev: PCI device + * @adapter: contains I2C adapter information + * @register_save_area: All relevant register are saved here during suspend + * @decoder: contains information about video DAC + * @format: pixel format, fixed UYVY + * @std: video standard (e.g. PAL/NTSC) + * @input: input line for video signal ( 0 or 1 ) + * @users: Number of open of device ( max. 1 ) + * @disabled: Device is in power down state + * @mutex: ensures exclusive opening of device + * @slock: for excluse acces of registers + * @vb_vidq: queue maintained by videobuf layer + * @capture: linked list of capture buffer + * @active: struct videobuf_buffer currently beingg filled + * @started: device is ready to capture frame + * @closing: device will be shut down + * @tcount: Number of top frames + * @bcount: Number of bottom frames + * @overflow: Number of FIFO overflows + * @mem_spare: small buffer of unused frame + * @dma_spare: dma addres of mem_spare + * @iomem: hardware base address + * @config: I2C and gpio config from platform + * + * All non-local data is accessed via this structure. + */ + +struct sta2x11_vip { + struct v4l2_device v4l2_dev; + struct video_device *video_dev; + struct pci_dev *pdev; + struct i2c_adapter *adapter; + unsigned int register_save_area[IRQ_COUNT + SAVE_COUNT + AUX_COUNT]; + struct v4l2_subdev *decoder; + struct v4l2_pix_format format; + v4l2_std_id std; + unsigned int input; + int users; + int disabled; + struct mutex mutex; /* exclusive access during open */ + spinlock_t slock; /* spin lock for hardware and queue access */ + struct videobuf_queue vb_vidq; + struct list_head capture; + struct videobuf_buffer *active; + int started, closing, tcount, bcount; + int overflow; + void *mem_spare; + dma_addr_t dma_spare; + void *iomem; + struct vip_config *config; +}; + +static const unsigned int registers_to_save[AUX_COUNT] = { + DVP_HLFLN, DVP_RGB, DVP_PKZ +}; + +static struct v4l2_pix_format formats_50[] = { + { /*PAL interlaced */ + .width = 720, + .height = 576, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 576, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + { /*PAL top */ + .width = 720, + .height = 288, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_TOP, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 288, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + { /*PAL bottom */ + .width = 720, + .height = 288, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_BOTTOM, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 288, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + +}; + +static struct v4l2_pix_format formats_60[] = { + { /*NTSC interlaced */ + .width = 720, + .height = 480, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_INTERLACED, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 480, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + { /*NTSC top */ + .width = 720, + .height = 240, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_TOP, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 240, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, + { /*NTSC bottom */ + .width = 720, + .height = 240, + .pixelformat = V4L2_PIX_FMT_UYVY, + .field = V4L2_FIELD_BOTTOM, + .bytesperline = 720 * 2, + .sizeimage = 720 * 2 * 240, + .colorspace = V4L2_COLORSPACE_SMPTE170M}, +}; + +/** + * buf_setup - Get size and number of video buffer + * @vq: queue in videobuf + * @count: Number of buffers (1..MAX_FRAMES). + * 0 use default value. + * @size: size of buffer in bytes + * + * returns size and number of buffers + * a preset value of 0 returns the default number. + * return value: 0, always succesfull. + */ +static int buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct sta2x11_vip *vip = vq->priv_data; + + *size = vip->format.width * vip->format.height * 2; + if (0 == *count || MAX_FRAMES < *count) + *count = MAX_FRAMES; + return 0; +}; + +/** + * buf_prepare - prepare buffer for usage + * @vq: queue in videobuf layer + * @vb: buffer to be prepared + * @field: type of video data (interlaced/non-interlaced) + * + * Allocate or realloc buffer + * return value: 0, successful. + * + * -EINVAL, supplied buffer is too small. + * + * other, buffer could not be locked. + */ +static int buf_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct sta2x11_vip *vip = vq->priv_data; + int ret; + + vb->size = vip->format.width * vip->format.height * 2; + if ((0 != vb->baddr) && (vb->bsize < vb->size)) + return -EINVAL; + vb->width = vip->format.width; + vb->height = vip->format.height; + vb->field = field; + + if (VIDEOBUF_NEEDS_INIT == vb->state) { + ret = videobuf_iolock(vq, vb, NULL); + if (ret) + goto fail; + } + vb->state = VIDEOBUF_PREPARED; + return 0; +fail: + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; + return ret; +} + +/** + * buf_queu - queue buffer for filling + * @vq: queue in videobuf layer + * @vb: buffer to be queued + * + * if capturing is already running, the buffer will be queued. Otherwise + * capture is started and the buffer is used directly. + */ +static void buf_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct sta2x11_vip *vip = vq->priv_data; + u32 dma; + + vb->state = VIDEOBUF_QUEUED; + + if (vip->active) { + list_add_tail(&vb->queue, &vip->capture); + return; + } + + vip->started = 1; + vip->tcount = 0; + vip->bcount = 0; + vip->active = vb; + vb->state = VIDEOBUF_ACTIVE; + + dma = videobuf_to_dma_contig(vb); + + REG_WRITE(vip, DVP_TFO, (0 << 16) | (0)); + /* despite of interlace mode, upper and lower frames start at zero */ + REG_WRITE(vip, DVP_BFO, (0 << 16) | (0)); + + switch (vip->format.field) { + case V4L2_FIELD_INTERLACED: + REG_WRITE(vip, DVP_TFS, + ((vip->format.height / 2 - 1) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_BFS, ((vip->format.height / 2 - 1) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); + REG_WRITE(vip, DVP_VMP, 4 * vip->format.width); + break; + case V4L2_FIELD_TOP: + REG_WRITE(vip, DVP_TFS, + ((vip->format.height - 1) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_BFS, ((0) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma); + REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); + break; + case V4L2_FIELD_BOTTOM: + REG_WRITE(vip, DVP_TFS, ((0) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_BFS, + ((vip->format.height) << 16) | + (2 * vip->format.width - 1)); + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma); + REG_WRITE(vip, DVP_VMP, 2 * vip->format.width); + break; + + default: + pr_warning("VIP: unknown field format\n"); + return; + } + + REG_WRITE(vip, DVP_CTL, DVP_CTL_ENA); +} + +/** + * buff_release - release buffer + * @vq: queue in videobuf layer + * @vb: buffer to be released + * + * release buffer in videobuf layer + */ +static void buf_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops vip_qops = { + .buf_setup = buf_setup, + .buf_prepare = buf_prepare, + .buf_queue = buf_queue, + .buf_release = buf_release, +}; + +/** + * vip_open - open video device + * @file: descriptor of device + * + * open device, make sure it is only opened once. + * return value: 0, no error. + * + * -EBUSY, device is already opened + * + * -ENOMEM, no memory for auxiliary DMA buffer + */ +static int vip_open(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct sta2x11_vip *vip = video_get_drvdata(dev); + + mutex_lock(&vip->mutex); + vip->users++; + + if (vip->users > 1) { + vip->users--; + mutex_unlock(&vip->mutex); + return -EBUSY; + } + + file->private_data = dev; + vip->overflow = 0; + vip->started = 0; + vip->closing = 0; + vip->active = NULL; + + INIT_LIST_HEAD(&vip->capture); + vip->mem_spare = dma_alloc_coherent(&vip->pdev->dev, 64, + &vip->dma_spare, GFP_KERNEL); + if (!vip->mem_spare) { + vip->users--; + mutex_unlock(&vip->mutex); + return -ENOMEM; + } + + mutex_unlock(&vip->mutex); + videobuf_queue_dma_contig_init_cached(&vip->vb_vidq, + &vip_qops, + &vip->pdev->dev, + &vip->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct videobuf_buffer), + vip, NULL); + REG_READ(vip, DVP_ITS); + REG_WRITE(vip, DVP_HLFLN, DVP_HLFLN_SD); + REG_WRITE(vip, DVP_ITM, DVP_IT_VSB | DVP_IT_VST); + REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); + REG_WRITE(vip, DVP_CTL, 0); + REG_READ(vip, DVP_ITS); + return 0; +} + +/** + * vip_close - close video device + * @file: descriptor of device + * + * close video device, wait until all pending operations are finished + * ( maximum FRAME_MAX buffers pending ) + * Turn off interrupts. + * + * return value: 0, always succesful. + */ +static int vip_close(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct sta2x11_vip *vip = video_get_drvdata(dev); + + vip->closing = 1; + if (vip->active) + videobuf_waiton(&vip->vb_vidq, vip->active, 0, 0); + spin_lock_irq(&vip->slock); + + REG_WRITE(vip, DVP_ITM, 0); + REG_WRITE(vip, DVP_CTL, DVP_CTL_RST); + REG_WRITE(vip, DVP_CTL, 0); + REG_READ(vip, DVP_ITS); + + vip->started = 0; + vip->active = NULL; + + spin_unlock_irq(&vip->slock); + + videobuf_stop(&vip->vb_vidq); + videobuf_mmap_free(&vip->vb_vidq); + + dma_free_coherent(&vip->pdev->dev, 64, vip->mem_spare, vip->dma_spare); + file->private_data = NULL; + mutex_lock(&vip->mutex); + vip->users--; + mutex_unlock(&vip->mutex); + return 0; +} + +/** + * vip_read - read from video input + * @file: descriptor of device + * @data: user buffer + * @count: number of bytes to be read + * @ppos: position within stream + * + * read video data from video device. + * handling is done in generic videobuf layer + * return value: provided by videobuf layer + */ +static ssize_t vip_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct video_device *dev = file->private_data; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_read_stream(&vip->vb_vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +/** + * vip_mmap - map user buffer + * @file: descriptor of device + * @vma: user buffer + * + * map user space buffer into kernel mode, including DMA address. + * handling is done in generic videobuf layer. + * return value: provided by videobuf layer + */ +static int vip_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_mmap_mapper(&vip->vb_vidq, vma); +} + +/** + * vip_poll - poll for event + * @file: descriptor of device + * @wait: contains events to be waited for + * + * wait for event related to video device. + * handling is done in generic videobuf layer. + * return value: provided by videobuf layer + */ +static unsigned int vip_poll(struct file *file, struct poll_table_struct *wait) +{ + struct video_device *dev = file->private_data; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_poll_stream(file, &vip->vb_vidq, wait); +} + +/** + * vidioc_querycap - return capabilities of device + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @cap: contains return values + * + * the capabilities of the device are returned + * + * return value: 0, no error. + */ +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + memset(cap, 0, sizeof(struct v4l2_capability)); + strcpy(cap->driver, DRV_NAME); + strcpy(cap->card, DRV_NAME); + cap->version = 0; + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s", + pci_name(vip->pdev)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + + return 0; +} + +/** + * vidioc_s_std - set video standard + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @std: contains standard to be set + * + * the video standard is set + * + * return value: 0, no error. + * + * -EIO, no input signal detected + * + * other, returned from video DAC. + */ +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + v4l2_std_id oldstd = vip->std, newstd; + int status; + + if (V4L2_STD_ALL == *std) { + v4l2_subdev_call(vip->decoder, core, s_std, *std); + ssleep(2); + v4l2_subdev_call(vip->decoder, video, querystd, &newstd); + v4l2_subdev_call(vip->decoder, video, g_input_status, &status); + if (status & V4L2_IN_ST_NO_SIGNAL) + return -EIO; + *std = vip->std = newstd; + if (oldstd != *std) { + if (V4L2_STD_525_60 & (*std)) + vip->format = formats_60[0]; + else + vip->format = formats_50[0]; + } + return 0; + } + + if (oldstd != *std) { + if (V4L2_STD_525_60 & (*std)) + vip->format = formats_60[0]; + else + vip->format = formats_50[0]; + } + + return v4l2_subdev_call(vip->decoder, core, s_std, *std); +} + +/** + * vidioc_g_std - get video standard + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @std: contains return values + * + * the current video standard is returned + * + * return value: 0, no error. + */ +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + *std = vip->std; + return 0; +} + +/** + * vidioc_querystd - get possible video standards + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @std: contains return values + * + * all possible video standards are returned + * + * return value: delivered by video DAC routine. + */ +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return v4l2_subdev_call(vip->decoder, video, querystd, std); + +} + +/** + * vidioc_queryctl - get possible control settings + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @ctrl: contains return values + * + * return possible values for a control + * return value: delivered by video DAC routine. + */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *ctrl) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return v4l2_subdev_call(vip->decoder, core, queryctrl, ctrl); +} + +/** + * vidioc_g_ctl - get control value + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @ctrl: contains return values + * + * return setting for a control value + * return value: delivered by video DAC routine. + */ +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return v4l2_subdev_call(vip->decoder, core, g_ctrl, ctrl); +} + +/** + * vidioc_s_ctl - set control value + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @ctrl: contains value to be set + * + * set value for a specific control + * return value: delivered by video DAC routine. + */ +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return v4l2_subdev_call(vip->decoder, core, s_ctrl, ctrl); +} + +/** + * vidioc_enum_input - return name of input line + * @file: descriptor of device (not used) + * @priv: points to current videodevice + * @inp: contains return values + * + * the user friendly name of the input line is returned + * + * return value: 0, no error. + * + * -EINVAL, input line number out of range + */ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + if (inp->index > 1) + return -EINVAL; + + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = V4L2_STD_ALL; + sprintf(inp->name, "Camera %u", inp->index); + + return 0; +} + +/** + * vidioc_s_input - set input line + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @i: new input line number + * + * the current active input line is set + * + * return value: 0, no error. + * + * -EINVAL, line number out of range + */ +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + int ret; + + if (i > 1) + return -EINVAL; + ret = v4l2_subdev_call(vip->decoder, video, s_routing, i, 0, 0); + + if (!ret) + vip->input = i; + + return 0; +} + +/** + * vidioc_g_input - return input line + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @i: returned input line number + * + * the current active input line is returned + * + * return value: always 0. + */ +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + *i = vip->input; + return 0; +} + +/** + * vidioc_enum_fmt_vid_cap - return video capture format + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @f: returned format information + * + * returns name and format of video capture + * Only UYVY is supported by hardware. + * + * return value: always 0. + */ +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + + if (f->index != 0) + return -EINVAL; + + strcpy(f->description, "4:2:2, packed, UYVY"); + f->pixelformat = V4L2_PIX_FMT_UYVY; + f->flags = 0; + return 0; +} + +/** + * vidioc_try_fmt_vid_cap - set video capture format + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @f: new format + * + * new video format is set which includes width and + * field type. width is fixed to 720, no scaling. + * Only UYVY is supported by this hardware. + * the minimum height is 200, the maximum is 576 (PAL) + * + * return value: 0, no error + * + * -EINVAL, pixel or field format not supported + * + */ +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + int interlace_lim; + + if (V4L2_PIX_FMT_UYVY != f->fmt.pix.pixelformat) + return -EINVAL; + + if (V4L2_STD_525_60 & vip->std) + interlace_lim = 240; + else + interlace_lim = 288; + + switch (f->fmt.pix.field) { + case V4L2_FIELD_ANY: + if (interlace_lim < f->fmt.pix.height) + f->fmt.pix.field = V4L2_FIELD_INTERLACED; + else + f->fmt.pix.field = V4L2_FIELD_BOTTOM; + break; + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + if (interlace_lim < f->fmt.pix.height) + f->fmt.pix.height = interlace_lim; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + f->fmt.pix.height &= ~1; + if (2 * interlace_lim < f->fmt.pix.height) + f->fmt.pix.height = 2 * interlace_lim; + if (200 > f->fmt.pix.height) + f->fmt.pix.height = 200; + f->fmt.pix.width = 720; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.width * 2 * f->fmt.pix.height; + f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + f->fmt.pix.priv = 0; + return 0; +} + +/** + * vidioc_s_fmt_vid_cap - set current video format parameters + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @f: returned format information + * + * set new capture format + * return value: 0, no error + * + * other, delivered by video DAC routine. + */ +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + int ret; + + ret = vidioc_try_fmt_vid_cap(file, priv, f); + if (ret) + return ret; + + memcpy(&vip->format, &f->fmt.pix, sizeof(struct v4l2_pix_format)); + return 0; +} + +/** + * vidioc_g_fmt_vid_cap - get current video format parameters + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @f: contains format information + * + * returns current video format parameters + * + * return value: 0, always successful + */ +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + memcpy(&f->fmt.pix, &vip->format, sizeof(struct v4l2_pix_format)); + return 0; +} + +/** + * vidioc_reqfs - request buffer + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @p: video buffer + * + * Handling is done in generic videobuf layer. + */ +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_reqbufs(&vip->vb_vidq, p); +} + +/** + * vidioc_querybuf - query buffer + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @p: video buffer + * + * query buffer state. + * Handling is done in generic videobuf layer. + */ +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_querybuf(&vip->vb_vidq, p); +} + +/** + * vidioc_qbuf - queue a buffer + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @p: video buffer + * + * Handling is done in generic videobuf layer. + */ +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_qbuf(&vip->vb_vidq, p); +} + +/** + * vidioc_dqbuf - dequeue a buffer + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @p: video buffer + * + * Handling is done in generic videobuf layer. + */ +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_dqbuf(&vip->vb_vidq, p, file->f_flags & O_NONBLOCK); +} + +/** + * vidioc_streamon - turn on streaming + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @type: type of capture + * + * turn on streaming. + * Handling is done in generic videobuf layer. + */ +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_streamon(&vip->vb_vidq); +} + +/** + * vidioc_streamoff - turn off streaming + * @file: descriptor of device ( not used) + * @priv: points to current videodevice + * @type: type of capture + * + * turn off streaming. + * Handling is done in generic videobuf layer. + */ +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct video_device *dev = priv; + struct sta2x11_vip *vip = video_get_drvdata(dev); + + return videobuf_streamoff(&vip->vb_vidq); +} + +static const struct v4l2_file_operations vip_fops = { + .owner = THIS_MODULE, + .open = vip_open, + .release = vip_close, + .ioctl = video_ioctl2, + .read = vip_read, + .mmap = vip_mmap, + .poll = vip_poll +}; + +static const struct v4l2_ioctl_ops vip_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_s_std = vidioc_s_std, + .vidioc_g_std = vidioc_g_std, + .vidioc_querystd = vidioc_querystd, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, +}; + +static struct video_device video_dev_template = { + .name = DRV_NAME, + .release = video_device_release, + .fops = &vip_fops, + .ioctl_ops = &vip_ioctl_ops, + .tvnorms = V4L2_STD_ALL, +}; + +/** + * vip_irq - interrupt routine + * @irq: Number of interrupt ( not used, correct number is assumed ) + * @vip: local data structure containing all information + * + * check for both frame interrupts set ( top and bottom ). + * check FIFO overflow, but limit number of log messages after open. + * signal a complete buffer if done. + * dequeue a new buffer if available. + * disable VIP if no buffer available. + * + * return value: IRQ_NONE, interrupt was not generated by VIP + * + * IRQ_HANDLED, interrupt done. + */ +static irqreturn_t vip_irq(int irq, struct sta2x11_vip *vip) +{ + u32 status, dma; + unsigned long flags; + struct videobuf_buffer *vb; + + status = REG_READ(vip, DVP_ITS); + + if (!status) { + pr_debug("VIP: irq ignored\n"); + return IRQ_NONE; + } + + if (!vip->started) + return IRQ_HANDLED; + + if (status & DVP_IT_VSB) + vip->bcount++; + + if (status & DVP_IT_VST) + vip->tcount++; + + if ((DVP_IT_VSB | DVP_IT_VST) == (status & (DVP_IT_VST | DVP_IT_VSB))) { + /* this is bad, we are too slow, hope the condition is gone + * on the next frame */ + pr_info("VIP: both irqs\n"); + return IRQ_HANDLED; + } + + if (status & DVP_IT_FIFO) { + if (5 > vip->overflow++) + pr_info("VIP: fifo overflow\n"); + } + + if (2 > vip->tcount) + return IRQ_HANDLED; + + if (status & DVP_IT_VSB) + return IRQ_HANDLED; + + spin_lock_irqsave(&vip->slock, flags); + + REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) & ~DVP_CTL_ENA); + if (vip->active) { + do_gettimeofday(&vip->active->ts); + vip->active->field_count++; + vip->active->state = VIDEOBUF_DONE; + wake_up(&vip->active->done); + vip->active = NULL; + } + if (!vip->closing) { + if (list_empty(&vip->capture)) + goto done; + + vb = list_first_entry(&vip->capture, struct videobuf_buffer, + queue); + if (NULL == vb) { + pr_info("VIP: no buffer\n"); + goto done; + } + vb->state = VIDEOBUF_ACTIVE; + list_del(&vb->queue); + vip->active = vb; + dma = videobuf_to_dma_contig(vb); + switch (vip->format.field) { + case V4L2_FIELD_INTERLACED: + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma + vip->format.width * 2); + break; + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + REG_WRITE(vip, DVP_VTP, dma); + REG_WRITE(vip, DVP_VBP, dma); + break; + default: + pr_warning("VIP: unknown field format\n"); + goto done; + break; + } + REG_WRITE(vip, DVP_CTL, REG_READ(vip, DVP_CTL) | DVP_CTL_ENA); + } +done: + spin_unlock_irqrestore(&vip->slock, flags); + return IRQ_HANDLED; +} + +/** + * vip_gpio_reserve - reserve gpio pin + * @dev: device + * @pin: GPIO pin number + * @dir: direction, input or output + * @name: GPIO pin name + * + */ +static int vip_gpio_reserve(struct device *dev, int pin, int dir, + const char *name) +{ + int ret; + + if (pin == -1) + return 0; + + ret = gpio_request(pin, name); + if (ret) { + dev_err(dev, "Failed to allocate pin %d (%s)\n", pin, name); + return ret; + } + + ret = gpio_direction_output(pin, dir); + if (ret) { + dev_err(dev, "Failed to set direction for pin %d (%s)\n", + pin, name); + gpio_free(pin); + return ret; + } + + ret = gpio_export(pin, false); + if (ret) { + dev_err(dev, "Failed to export pin %d (%s)\n", pin, name); + gpio_free(pin); + return ret; + } + + return 0; +} + +/** + * vip_gpio_release - release gpio pin + * @dev: device + * @pin: GPIO pin number + * @name: GPIO pin name + * + */ +static void vip_gpio_release(struct device *dev, int pin, const char *name) +{ + if (pin != -1) { + dev_dbg(dev, "releasing pin %d (%s)\n", pin, name); + gpio_unexport(pin); + gpio_free(pin); + } +} + +/** + * sta2x11_vip_init_one - init one instance of video device + * @pdev: PCI device + * @ent: (not used) + * + * allocate reset pins for DAC. + * Reset video DAC, this is done via reset line. + * allocate memory for managing device + * request interrupt + * map IO region + * register device + * find and initialize video DAC + * + * return value: 0, no error + * + * -ENOMEM, no memory + * + * -ENODEV, device could not be detected or registered + */ +static int __devinit sta2x11_vip_init_one(struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret; + struct sta2x11_vip *vip; + struct vip_config *config; + + ret = pci_enable_device(pdev); + if (ret) + return ret; + + config = dev_get_platdata(&pdev->dev); + if (!config) { + dev_info(&pdev->dev, "VIP slot disabled\n"); + ret = -EINVAL; + goto disable; + } + + ret = vip_gpio_reserve(&pdev->dev, config->pwr_pin, 0, + config->pwr_name); + if (ret) + goto disable; + + if (config->reset_pin >= 0) { + ret = vip_gpio_reserve(&pdev->dev, config->reset_pin, 0, + config->reset_name); + if (ret) { + vip_gpio_release(&pdev->dev, config->pwr_pin, + config->pwr_name); + goto disable; + } + } + + if (config->pwr_pin != -1) { + /* Datasheet says 5ms between PWR and RST */ + usleep_range(5000, 25000); + ret = gpio_direction_output(config->pwr_pin, 1); + } + + if (config->reset_pin != -1) { + /* Datasheet says 5ms between PWR and RST */ + usleep_range(5000, 25000); + ret = gpio_direction_output(config->reset_pin, 1); + } + usleep_range(5000, 25000); + + vip = kzalloc(sizeof(struct sta2x11_vip), GFP_KERNEL); + if (!vip) { + ret = -ENOMEM; + goto release_gpios; + } + + vip->pdev = pdev; + vip->std = V4L2_STD_PAL; + vip->format = formats_50[0]; + vip->config = config; + + if (v4l2_device_register(&pdev->dev, &vip->v4l2_dev)) + goto free_mem; + + dev_dbg(&pdev->dev, "BAR #0 at 0x%lx 0x%lx irq %d\n", + (unsigned long)pci_resource_start(pdev, 0), + (unsigned long)pci_resource_len(pdev, 0), pdev->irq); + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, DRV_NAME); + if (ret) + goto unreg; + + vip->iomem = pci_iomap(pdev, 0, 0x100); + if (!vip->iomem) { + ret = -ENOMEM; /* FIXME */ + goto release; + } + + pci_enable_msi(pdev); + + INIT_LIST_HEAD(&vip->capture); + spin_lock_init(&vip->slock); + mutex_init(&vip->mutex); + vip->started = 0; + vip->disabled = 0; + + ret = request_irq(pdev->irq, + (irq_handler_t) vip_irq, + IRQF_SHARED, DRV_NAME, vip); + if (ret) { + dev_err(&pdev->dev, "request_irq failed\n"); + ret = -ENODEV; + goto unmap; + } + + vip->video_dev = video_device_alloc(); + if (!vip->video_dev) { + ret = -ENOMEM; + goto release_irq; + } + + *(vip->video_dev) = video_dev_template; + video_set_drvdata(vip->video_dev, vip); + + ret = video_register_device(vip->video_dev, VFL_TYPE_GRABBER, -1); + if (ret) + goto vrelease; + + vip->adapter = i2c_get_adapter(vip->config->i2c_id); + if (!vip->adapter) { + ret = -ENODEV; + dev_err(&pdev->dev, "no I2C adapter found\n"); + goto vunreg; + } + + vip->decoder = v4l2_i2c_new_subdev(&vip->v4l2_dev, vip->adapter, + "adv7180", vip->config->i2c_addr, + NULL); + if (!vip->decoder) { + ret = -ENODEV; + dev_err(&pdev->dev, "no decoder found\n"); + goto vunreg; + } + + i2c_put_adapter(vip->adapter); + + v4l2_subdev_call(vip->decoder, core, init, 0); + + pr_info("STA2X11 Video Input Port (VIP) loaded\n"); + return 0; + +vunreg: + video_set_drvdata(vip->video_dev, NULL); +vrelease: + if (video_is_registered(vip->video_dev)) + video_unregister_device(vip->video_dev); + else + video_device_release(vip->video_dev); +release_irq: + free_irq(pdev->irq, vip); + pci_disable_msi(pdev); +unmap: + pci_iounmap(pdev, vip->iomem); + mutex_destroy(&vip->mutex); +release: + pci_release_regions(pdev); +unreg: + v4l2_device_unregister(&vip->v4l2_dev); +free_mem: + kfree(vip); +release_gpios: + vip_gpio_release(&pdev->dev, config->reset_pin, config->reset_name); + vip_gpio_release(&pdev->dev, config->pwr_pin, config->pwr_name); +disable: + /* + * do not call pci_disable_device on sta2x11 because it break all + * other Bus masters on this EP + */ + return ret; +} + +/** + * sta2x11_vip_remove_one - release device + * @pdev: PCI device + * + * Undo everything done in .._init_one + * + * unregister video device + * free interrupt + * unmap ioadresses + * free memory + * free GPIO pins + */ +static void __devexit sta2x11_vip_remove_one(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct sta2x11_vip *vip = + container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); + + video_set_drvdata(vip->video_dev, NULL); + video_unregister_device(vip->video_dev); + /*do not call video_device_release() here, is already done */ + free_irq(pdev->irq, vip); + pci_disable_msi(pdev); + pci_iounmap(pdev, vip->iomem); + pci_release_regions(pdev); + + v4l2_device_unregister(&vip->v4l2_dev); + mutex_destroy(&vip->mutex); + + vip_gpio_release(&pdev->dev, vip->config->pwr_pin, + vip->config->pwr_name); + vip_gpio_release(&pdev->dev, vip->config->reset_pin, + vip->config->reset_name); + + kfree(vip); + /* + * do not call pci_disable_device on sta2x11 because it break all + * other Bus masters on this EP + */ +} + +#ifdef CONFIG_PM + +/** + * sta2x11_vip_suspend - set device into power save mode + * @pdev: PCI device + * @state: new state of device + * + * all relevant registers are saved and an attempt to set a new state is made. + * + * return value: 0 always indicate success, + * even if device could not be disabled. (workaround for hardware problem) + * + * reurn value : 0, always succesful, even if hardware does not not support + * power down mode. + */ +static int sta2x11_vip_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct sta2x11_vip *vip = + container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); + unsigned long flags; + int i; + + spin_lock_irqsave(&vip->slock, flags); + vip->register_save_area[0] = REG_READ(vip, DVP_CTL); + REG_WRITE(vip, DVP_CTL, vip->register_save_area[0] & DVP_CTL_DIS); + vip->register_save_area[SAVE_COUNT] = REG_READ(vip, DVP_ITM); + REG_WRITE(vip, DVP_ITM, 0); + for (i = 1; i < SAVE_COUNT; i++) + vip->register_save_area[i] = REG_READ(vip, 4 * i); + for (i = 0; i < AUX_COUNT; i++) + vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i] = + REG_READ(vip, registers_to_save[i]); + spin_unlock_irqrestore(&vip->slock, flags); + /* save pci state */ + pci_save_state(pdev); + if (pci_set_power_state(pdev, pci_choose_state(pdev, state))) { + /* + * do not call pci_disable_device on sta2x11 because it + * break all other Bus masters on this EP + */ + vip->disabled = 1; + } + + pr_info("VIP: suspend\n"); + return 0; +} + +/** + * sta2x11_vip_resume - resume device operation + * @pdev : PCI device + * + * re-enable device, set PCI state to powered and restore registers. + * resume normal device operation afterwards. + * + * return value: 0, no error. + * + * other, could not set device to power on state. + */ +static int sta2x11_vip_resume(struct pci_dev *pdev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev); + struct sta2x11_vip *vip = + container_of(v4l2_dev, struct sta2x11_vip, v4l2_dev); + unsigned long flags; + int ret, i; + + pr_info("VIP: resume\n"); + /* restore pci state */ + if (vip->disabled) { + ret = pci_enable_device(pdev); + if (ret) { + pr_warning("VIP: Can't enable device.\n"); + return ret; + } + vip->disabled = 0; + } + ret = pci_set_power_state(pdev, PCI_D0); + if (ret) { + /* + * do not call pci_disable_device on sta2x11 because it + * break all other Bus masters on this EP + */ + pr_warning("VIP: Can't enable device.\n"); + vip->disabled = 1; + return ret; + } + + pci_restore_state(pdev); + + spin_lock_irqsave(&vip->slock, flags); + for (i = 1; i < SAVE_COUNT; i++) + REG_WRITE(vip, 4 * i, vip->register_save_area[i]); + for (i = 0; i < AUX_COUNT; i++) + REG_WRITE(vip, registers_to_save[i], + vip->register_save_area[SAVE_COUNT + IRQ_COUNT + i]); + REG_WRITE(vip, DVP_CTL, vip->register_save_area[0]); + REG_WRITE(vip, DVP_ITM, vip->register_save_area[SAVE_COUNT]); + spin_unlock_irqrestore(&vip->slock, flags); + return 0; +} + +#endif + +static DEFINE_PCI_DEVICE_TABLE(sta2x11_vip_pci_tbl) = { + {PCI_DEVICE(PCI_VENDOR_ID_STMICRO, PCI_DEVICE_ID_STMICRO_VIP)}, + {0,} +}; + +static struct pci_driver sta2x11_vip_driver = { + .name = DRV_NAME, + .probe = sta2x11_vip_init_one, + .remove = __devexit_p(sta2x11_vip_remove_one), + .id_table = sta2x11_vip_pci_tbl, +#ifdef CONFIG_PM + .suspend = sta2x11_vip_suspend, + .resume = sta2x11_vip_resume, +#endif +}; + +static int __init sta2x11_vip_init_module(void) +{ + return pci_register_driver(&sta2x11_vip_driver); +} + +static void __exit sta2x11_vip_exit_module(void) +{ + pci_unregister_driver(&sta2x11_vip_driver); +} + +#ifdef MODULE +module_init(sta2x11_vip_init_module); +module_exit(sta2x11_vip_exit_module); +#else +late_initcall_sync(sta2x11_vip_init_module); +#endif + +MODULE_DESCRIPTION("STA2X11 Video Input Port driver"); +MODULE_AUTHOR("Wind River"); +MODULE_LICENSE("GPL v2"); +MODULE_SUPPORTED_DEVICE("sta2x11 video input"); +MODULE_VERSION(DRV_VERSION); +MODULE_DEVICE_TABLE(pci, sta2x11_vip_pci_tbl); diff --git a/drivers/media/video/sta2x11_vip.h b/drivers/media/video/sta2x11_vip.h new file mode 100644 index 000000000000..4f81a13666eb --- /dev/null +++ b/drivers/media/video/sta2x11_vip.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2011 Wind River Systems, Inc. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Author: Anders Wallin + * + */ + +#ifndef __STA2X11_VIP_H +#define __STA2X11_VIP_H + +/** + * struct vip_config - video input configuration data + * @pwr_name: ADV powerdown name + * @pwr_pin: ADV powerdown pin + * @reset_name: ADV reset name + * @reset_pin: ADV reset pin + */ +struct vip_config { + const char *pwr_name; + int pwr_pin; + const char *reset_name; + int reset_pin; + int i2c_id; + int i2c_addr; +}; + +#endif /* __STA2X11_VIP_H */ diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c index d427f8436c70..86a0fc56c330 100644 --- a/drivers/media/video/stk-webcam.c +++ b/drivers/media/video/stk-webcam.c @@ -38,13 +38,13 @@ #include "stk-webcam.h" -static bool hflip = 1; +static bool hflip; module_param(hflip, bool, 0444); -MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 1"); +MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); -static bool vflip = 1; +static bool vflip; module_param(vflip, bool, 0444); -MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 1"); +MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); static int debug; module_param(debug, int, 0444); diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c index 465d7086babf..3d7ddd93282d 100644 --- a/drivers/media/video/tda9840.c +++ b/drivers/media/video/tda9840.c @@ -66,41 +66,11 @@ static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val) val, reg); } -static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) -{ - int byte; - - if (t->index) - return -EINVAL; - - switch (t->audmode) { - case V4L2_TUNER_MODE_STEREO: - byte = TDA9840_SET_STEREO; - break; - case V4L2_TUNER_MODE_LANG1_LANG2: - byte = TDA9840_SET_BOTH; - break; - case V4L2_TUNER_MODE_LANG1: - byte = TDA9840_SET_LANG1; - break; - case V4L2_TUNER_MODE_LANG2: - byte = TDA9840_SET_LANG2; - break; - default: - byte = TDA9840_SET_MONO; - break; - } - v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); - tda9840_write(sd, SWITCH, byte); - return 0; -} - -static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +static int tda9840_status(struct v4l2_subdev *sd) { struct i2c_client *client = v4l2_get_subdevdata(sd); u8 byte; - t->rxsubchans = V4L2_TUNER_SUB_MONO; if (1 != i2c_master_recv(client, &byte, 1)) { v4l2_dbg(1, debug, sd, "i2c_master_recv() failed\n"); @@ -114,8 +84,51 @@ static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) } v4l2_dbg(1, debug, sd, "TDA9840_DETECT: byte: 0x%02x\n", byte); + return byte & 0x60; +} - switch (byte & 0x60) { +static int tda9840_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +{ + int stat = tda9840_status(sd); + int byte; + + if (t->index) + return -EINVAL; + + stat = stat < 0 ? 0 : stat; + if (stat == 0 || stat == 0x60) /* mono input */ + byte = TDA9840_SET_MONO; + else if (stat == 0x40) /* stereo input */ + byte = (t->audmode == V4L2_TUNER_MODE_MONO) ? + TDA9840_SET_MONO : TDA9840_SET_STEREO; + else { /* bilingual */ + switch (t->audmode) { + case V4L2_TUNER_MODE_LANG1_LANG2: + byte = TDA9840_SET_BOTH; + break; + case V4L2_TUNER_MODE_LANG2: + byte = TDA9840_SET_LANG2; + break; + default: + byte = TDA9840_SET_LANG1; + break; + } + } + v4l2_dbg(1, debug, sd, "TDA9840_SWITCH: 0x%02x\n", byte); + tda9840_write(sd, SWITCH, byte); + return 0; +} + +static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t) +{ + int stat = tda9840_status(sd); + + if (stat < 0) + return stat; + + t->rxsubchans = V4L2_TUNER_SUB_MONO; + + switch (stat & 0x60) { case 0x00: t->rxsubchans = V4L2_TUNER_SUB_MONO; break; diff --git a/drivers/media/video/tlg2300/pd-video.c b/drivers/media/video/tlg2300/pd-video.c index a794ae62aebf..bfbf9e56b0a4 100644 --- a/drivers/media/video/tlg2300/pd-video.c +++ b/drivers/media/video/tlg2300/pd-video.c @@ -150,7 +150,6 @@ static int vidioc_querycap(struct file *file, void *fh, strcpy(cap->driver, "tele-video"); strcpy(cap->card, "Telegent Poseidon"); usb_make_path(p->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->version = KERNEL_VERSION(0, 0, 1); cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE | V4L2_CAP_VBI_CAPTURE; diff --git a/drivers/media/video/tm6000/tm6000-input.c b/drivers/media/video/tm6000/tm6000-input.c index 859eb90e4d56..e80b7e190471 100644 --- a/drivers/media/video/tm6000/tm6000-input.c +++ b/drivers/media/video/tm6000/tm6000-input.c @@ -168,7 +168,6 @@ static void tm6000_ir_urb_received(struct urb *urb) struct tm6000_IR *ir = dev->ir; struct tm6000_ir_poll_result poll_result; char *buf; - int rc; dprintk(2, "%s\n",__func__); if (urb->status < 0 || urb->actual_length <= 0) { @@ -192,7 +191,7 @@ static void tm6000_ir_urb_received(struct urb *urb) dprintk(1, "%s, scancode: 0x%04x\n",__func__, poll_result.rc_data); rc_keydown(ir->rc, poll_result.rc_data, 0); - rc = usb_submit_urb(urb, GFP_ATOMIC); + usb_submit_urb(urb, GFP_ATOMIC); /* * Flash the led. We can't do it here, as it is running on IRQ context. * So, use the scheduler to do it, in a few ms. diff --git a/drivers/media/video/tm6000/tm6000-stds.c b/drivers/media/video/tm6000/tm6000-stds.c index 9dc0831d813f..5e28d6a2412f 100644 --- a/drivers/media/video/tm6000/tm6000-stds.c +++ b/drivers/media/video/tm6000/tm6000-stds.c @@ -338,7 +338,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev) uint8_t areg_02 = 0x04; /* GC1 Fixed gain 0dB */ uint8_t areg_05 = 0x01; /* Auto 4.5 = M Japan, Auto 6.5 = DK */ uint8_t areg_06 = 0x02; /* Auto de-emphasis, mannual channel mode */ - uint8_t nicam_flag = 0; /* No NICAM */ if (dev->radio) { tm6000_set_reg(dev, TM6010_REQ08_R01_A_INIT, 0x00); @@ -398,7 +397,6 @@ static int tm6000_set_audio_std(struct tm6000_core *dev) } else { areg_05 = 0x07; } - nicam_flag = 1; break; /* other */ case 3: diff --git a/drivers/media/video/tm6000/tm6000-video.c b/drivers/media/video/tm6000/tm6000-video.c index bc13db736e24..f7034df94e0a 100644 --- a/drivers/media/video/tm6000/tm6000-video.c +++ b/drivers/media/video/tm6000/tm6000-video.c @@ -169,7 +169,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, struct tm6000_buffer **buf) { struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - char *outp; if (list_empty(&dma_q->active)) { dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); @@ -179,11 +178,6 @@ static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, *buf = list_entry(dma_q->active.next, struct tm6000_buffer, vb.queue); - - /* Cleans up buffer - Useful for testing for frame/URB loss */ - outp = videobuf_to_vmalloc(&(*buf)->vb); - - return; } /* @@ -211,7 +205,7 @@ static int copy_streams(u8 *data, unsigned long len, { struct tm6000_dmaqueue *dma_q = urb->context; struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); - u8 *ptr = data, *endp = data+len, c; + u8 *ptr = data, *endp = data+len; unsigned long header = 0; int rc = 0; unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; @@ -264,7 +258,6 @@ static int copy_streams(u8 *data, unsigned long len, } /* split the header fields */ - c = (header >> 24) & 0xff; size = ((header & 0x7e) << 1); if (size > 0) size -= 4; @@ -889,7 +882,6 @@ static int vidioc_querycap(struct file *file, void *priv, strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); strlcpy(cap->card, "Trident TVMaster TM5600/6000/6010", sizeof(cap->card)); - cap->version = TM6000_VERSION; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_AUDIO | @@ -1732,6 +1724,10 @@ static struct video_device *vdev_init(struct tm6000_core *dev, vfd->release = video_device_release; vfd->debug = tm6000_debug; vfd->lock = &dev->lock; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vfd->flags); snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name); diff --git a/drivers/media/video/tm6000/tm6000.h b/drivers/media/video/tm6000/tm6000.h index 27ba659cfa85..6df418658c9c 100644 --- a/drivers/media/video/tm6000/tm6000.h +++ b/drivers/media/video/tm6000/tm6000.h @@ -33,8 +33,6 @@ #include "dvb_frontend.h" #include "dmxdev.h" -#define TM6000_VERSION KERNEL_VERSION(0, 0, 2) - /* Inputs */ enum tm6000_itype { TM6000_INPUT_TV = 1, diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c index a5c6397ad591..3e050e12153b 100644 --- a/drivers/media/video/tuner-core.c +++ b/drivers/media/video/tuner-core.c @@ -1241,8 +1241,10 @@ static int tuner_log_status(struct v4l2_subdev *sd) return 0; } -static int tuner_suspend(struct i2c_client *c, pm_message_t state) +#ifdef CONFIG_PM_SLEEP +static int tuner_suspend(struct device *dev) { + struct i2c_client *c = to_i2c_client(dev); struct tuner *t = to_tuner(i2c_get_clientdata(c)); struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops; @@ -1254,8 +1256,9 @@ static int tuner_suspend(struct i2c_client *c, pm_message_t state) return 0; } -static int tuner_resume(struct i2c_client *c) +static int tuner_resume(struct device *dev) { + struct i2c_client *c = to_i2c_client(dev); struct tuner *t = to_tuner(i2c_get_clientdata(c)); tuner_dbg("resume\n"); @@ -1266,6 +1269,7 @@ static int tuner_resume(struct i2c_client *c) return 0; } +#endif static int tuner_command(struct i2c_client *client, unsigned cmd, void *arg) { @@ -1310,6 +1314,10 @@ static const struct v4l2_subdev_ops tuner_ops = { * I2C structs and module init functions */ +static const struct dev_pm_ops tuner_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(tuner_suspend, tuner_resume) +}; + static const struct i2c_device_id tuner_id[] = { { "tuner", }, /* autodetect */ { } @@ -1320,12 +1328,11 @@ static struct i2c_driver tuner_driver = { .driver = { .owner = THIS_MODULE, .name = "tuner", + .pm = &tuner_pm_ops, }, .probe = tuner_probe, .remove = tuner_remove, .command = tuner_command, - .suspend = tuner_suspend, - .resume = tuner_resume, .id_table = tuner_id, }; diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c index 1326e11cf4a9..b7867427e5c4 100644 --- a/drivers/media/video/tvp5150.c +++ b/drivers/media/video/tvp5150.c @@ -822,7 +822,7 @@ static int tvp5150_enum_mbus_fmt(struct v4l2_subdev *sd, unsigned index, if (index) return -EINVAL; - *code = V4L2_MBUS_FMT_YUYV8_2X8; + *code = V4L2_MBUS_FMT_UYVY8_2X8; return 0; } @@ -830,23 +830,16 @@ static int tvp5150_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f) { struct tvp5150 *decoder = to_tvp5150(sd); - v4l2_std_id std; if (f == NULL) return -EINVAL; tvp5150_reset(sd, 0); - /* Calculate height and width based on current standard */ - if (decoder->norm == V4L2_STD_ALL) - std = tvp5150_read_std(sd); - else - std = decoder->norm; - f->width = decoder->rect.width; f->height = decoder->rect.height; - f->code = V4L2_MBUS_FMT_YUYV8_2X8; + f->code = V4L2_MBUS_FMT_UYVY8_2X8; f->field = V4L2_FIELD_SEQ_TB; f->colorspace = V4L2_COLORSPACE_SMPTE170M; diff --git a/drivers/media/video/tvp7002.c b/drivers/media/video/tvp7002.c index d7676d85c4df..fb6a5b57eb83 100644 --- a/drivers/media/video/tvp7002.c +++ b/drivers/media/video/tvp7002.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -328,6 +329,7 @@ static const struct i2c_reg_value tvp7002_parms_720P50[] = { /* Preset definition for handling device operation */ struct tvp7002_preset_definition { u32 preset; + struct v4l2_dv_timings timings; const struct i2c_reg_value *p_settings; enum v4l2_colorspace color_space; enum v4l2_field scanmode; @@ -341,6 +343,7 @@ struct tvp7002_preset_definition { static const struct tvp7002_preset_definition tvp7002_presets[] = { { V4L2_DV_720P60, + V4L2_DV_BT_CEA_1280X720P60, tvp7002_parms_720P60, V4L2_COLORSPACE_REC709, V4L2_FIELD_NONE, @@ -351,6 +354,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_1080I60, + V4L2_DV_BT_CEA_1920X1080I60, tvp7002_parms_1080I60, V4L2_COLORSPACE_REC709, V4L2_FIELD_INTERLACED, @@ -361,6 +365,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_1080I50, + V4L2_DV_BT_CEA_1920X1080I50, tvp7002_parms_1080I50, V4L2_COLORSPACE_REC709, V4L2_FIELD_INTERLACED, @@ -371,6 +376,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_720P50, + V4L2_DV_BT_CEA_1280X720P50, tvp7002_parms_720P50, V4L2_COLORSPACE_REC709, V4L2_FIELD_NONE, @@ -381,6 +387,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_1080P60, + V4L2_DV_BT_CEA_1920X1080P60, tvp7002_parms_1080P60, V4L2_COLORSPACE_REC709, V4L2_FIELD_NONE, @@ -391,6 +398,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_480P59_94, + V4L2_DV_BT_CEA_720X480P59_94, tvp7002_parms_480P, V4L2_COLORSPACE_SMPTE170M, V4L2_FIELD_NONE, @@ -401,6 +409,7 @@ static const struct tvp7002_preset_definition tvp7002_presets[] = { }, { V4L2_DV_576P50, + V4L2_DV_BT_CEA_720X576P50, tvp7002_parms_576P, V4L2_COLORSPACE_SMPTE170M, V4L2_FIELD_NONE, @@ -605,6 +614,35 @@ static int tvp7002_s_dv_preset(struct v4l2_subdev *sd, return -EINVAL; } +static int tvp7002_s_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct tvp7002 *device = to_tvp7002(sd); + const struct v4l2_bt_timings *bt = &dv_timings->bt; + int i; + + if (dv_timings->type != V4L2_DV_BT_656_1120) + return -EINVAL; + for (i = 0; i < NUM_PRESETS; i++) { + const struct v4l2_bt_timings *t = &tvp7002_presets[i].timings.bt; + + if (!memcmp(bt, t, &bt->standards - &bt->width)) { + device->current_preset = &tvp7002_presets[i]; + return tvp7002_write_inittab(sd, tvp7002_presets[i].p_settings); + } + } + return -EINVAL; +} + +static int tvp7002_g_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *dv_timings) +{ + struct tvp7002 *device = to_tvp7002(sd); + + *dv_timings = device->current_preset->timings; + return 0; +} + /* * tvp7002_s_ctrl() - Set a control * @ctrl: ptr to v4l2_ctrl struct @@ -666,11 +704,9 @@ static int tvp7002_mbus_fmt(struct v4l2_subdev *sd, struct v4l2_mbus_framefmt *f * Returns the current DV preset by TVP7002. If no active input is * detected, returns -EINVAL */ -static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, - struct v4l2_dv_preset *qpreset) +static int tvp7002_query_dv(struct v4l2_subdev *sd, int *index) { const struct tvp7002_preset_definition *presets = tvp7002_presets; - struct tvp7002 *device; u8 progressive; u32 lpfr; u32 cpln; @@ -679,12 +715,9 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, u8 lpf_msb; u8 cpl_lsb; u8 cpl_msb; - int index; - /* Return invalid preset if no active input is detected */ - qpreset->preset = V4L2_DV_INVALID; - - device = to_tvp7002(sd); + /* Return invalid index if no active input is detected */ + *index = NUM_PRESETS; /* Read standards from device registers */ tvp7002_read_err(sd, TVP7002_L_FRAME_STAT_LSBS, &lpf_lsb, &error); @@ -705,8 +738,8 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, progressive = (lpf_msb & TVP7002_INPR_MASK) >> TVP7002_IP_SHIFT; /* Do checking of video modes */ - for (index = 0; index < NUM_PRESETS; index++, presets++) - if (lpfr == presets->lines_per_frame && + for (*index = 0; *index < NUM_PRESETS; (*index)++, presets++) + if (lpfr == presets->lines_per_frame && progressive == presets->progressive) { if (presets->cpl_min == 0xffff) break; @@ -714,17 +747,42 @@ static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, break; } - if (index == NUM_PRESETS) { + if (*index == NUM_PRESETS) { v4l2_dbg(1, debug, sd, "detection failed: lpf = %x, cpl = %x\n", lpfr, cpln); - return 0; + return -ENOLINK; } - /* Set values in found preset */ - qpreset->preset = presets->preset; - /* Update lines per frame and clocks per line info */ - v4l2_dbg(1, debug, sd, "detected preset: %d\n", presets->preset); + v4l2_dbg(1, debug, sd, "detected preset: %d\n", *index); + return 0; +} + +static int tvp7002_query_dv_preset(struct v4l2_subdev *sd, + struct v4l2_dv_preset *qpreset) +{ + int index; + int err = tvp7002_query_dv(sd, &index); + + if (err || index == NUM_PRESETS) { + qpreset->preset = V4L2_DV_INVALID; + if (err == -ENOLINK) + err = 0; + return err; + } + qpreset->preset = tvp7002_presets[index].preset; + return 0; +} + +static int tvp7002_query_dv_timings(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings) +{ + int index; + int err = tvp7002_query_dv(sd, &index); + + if (err) + return err; + *timings = tvp7002_presets[index].timings; return 0; } @@ -894,6 +952,17 @@ static int tvp7002_enum_dv_presets(struct v4l2_subdev *sd, return v4l_fill_dv_preset_info(tvp7002_presets[preset->index].preset, preset); } +static int tvp7002_enum_dv_timings(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings) +{ + /* Check requested format index is within range */ + if (timings->index >= NUM_PRESETS) + return -EINVAL; + + timings->timings = tvp7002_presets[timings->index].timings; + return 0; +} + static const struct v4l2_ctrl_ops tvp7002_ctrl_ops = { .s_ctrl = tvp7002_s_ctrl, }; @@ -920,6 +989,10 @@ static const struct v4l2_subdev_video_ops tvp7002_video_ops = { .enum_dv_presets = tvp7002_enum_dv_presets, .s_dv_preset = tvp7002_s_dv_preset, .query_dv_preset = tvp7002_query_dv_preset, + .g_dv_timings = tvp7002_g_dv_timings, + .s_dv_timings = tvp7002_s_dv_timings, + .enum_dv_timings = tvp7002_enum_dv_timings, + .query_dv_timings = tvp7002_query_dv_timings, .s_stream = tvp7002_s_stream, .g_mbus_fmt = tvp7002_mbus_fmt, .try_mbus_fmt = tvp7002_mbus_fmt, diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c index f344411a4578..c9b2042f8bdf 100644 --- a/drivers/media/video/usbvision/usbvision-core.c +++ b/drivers/media/video/usbvision/usbvision-core.c @@ -601,13 +601,12 @@ static int usbvision_decompress(struct usb_usbvision *usbvision, unsigned char * unsigned char *decompressed, int *start_pos, int *block_typestart_pos, int len) { - int rest_pixel, idx, max_pos, pos, extra_pos, block_len, block_type_pos, block_type_len; + int rest_pixel, idx, pos, extra_pos, block_len, block_type_pos, block_type_len; unsigned char block_byte, block_code, block_type, block_type_byte, integrator; integrator = 0; pos = *start_pos; block_type_pos = *block_typestart_pos; - max_pos = 396; /* pos + len; */ extra_pos = pos; block_len = 0; block_byte = 0; @@ -702,7 +701,7 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision unsigned char strip_data[USBVISION_STRIP_LEN_MAX]; unsigned char strip_header[USBVISION_STRIP_HEADER_LEN]; int idx, idx_end, strip_len, strip_ptr, startblock_pos, block_pos, block_type_pos; - int clipmask_index, bytes_per_pixel, rc; + int clipmask_index; int image_size; unsigned char rv, gv, bv; static unsigned char *Y, *U, *V; @@ -769,7 +768,6 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision return parse_state_next_frame; } - bytes_per_pixel = frame->v4l2_format.bytes_per_pixel; clipmask_index = frame->curline * MAX_FRAME_WIDTH; scratch_get(usbvision, strip_data, strip_len); @@ -781,14 +779,14 @@ static enum parse_state usbvision_parse_compress(struct usb_usbvision *usbvision usbvision->block_pos = block_pos; - rc = usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); + usbvision_decompress(usbvision, strip_data, Y, &block_pos, &block_type_pos, idx_end); if (strip_len > usbvision->max_strip_len) usbvision->max_strip_len = strip_len; if (frame->curline % 2) - rc = usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); + usbvision_decompress(usbvision, strip_data, V, &block_pos, &block_type_pos, idx_end / 2); else - rc = usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); + usbvision_decompress(usbvision, strip_data, U, &block_pos, &block_type_pos, idx_end / 2); if (block_pos > usbvision->comprblock_pos) usbvision->comprblock_pos = block_pos; diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c index 5a74f5e07d7d..9bd8f084f348 100644 --- a/drivers/media/video/usbvision/usbvision-video.c +++ b/drivers/media/video/usbvision/usbvision-video.c @@ -1296,6 +1296,10 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision, if (NULL == vdev) return NULL; *vdev = *vdev_template; + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags); vdev->lock = &usbvision->v4l2_lock; vdev->v4l2_dev = &usbvision->v4l2_dev; snprintf(vdev->name, sizeof(vdev->name), "%s", name); diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c index 0efd3b10b353..af26bbe6f76e 100644 --- a/drivers/media/video/uvc/uvc_ctrl.c +++ b/drivers/media/video/uvc/uvc_ctrl.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "uvcvideo.h" @@ -420,6 +421,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_HUE_AUTO, + .master_manual = 0, }, { .id = V4L2_CID_SATURATION, @@ -492,6 +495,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_HUE, }, }, { .id = V4L2_CID_EXPOSURE_AUTO, @@ -504,6 +508,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .data_type = UVC_CTRL_DATA_TYPE_BITMASK, .menu_info = exposure_auto_controls, .menu_count = ARRAY_SIZE(exposure_auto_controls), + .slave_ids = { V4L2_CID_EXPOSURE_ABSOLUTE, }, }, { .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, @@ -524,6 +529,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_EXPOSURE_AUTO, + .master_manual = V4L2_EXPOSURE_MANUAL, }, { .id = V4L2_CID_AUTO_WHITE_BALANCE, @@ -534,6 +541,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_WHITE_BALANCE_TEMPERATURE, }, }, { .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, @@ -544,6 +552,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_AUTO_WHITE_BALANCE, @@ -554,6 +564,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_BLUE_BALANCE, + V4L2_CID_RED_BALANCE }, }, { .id = V4L2_CID_BLUE_BALANCE, @@ -564,6 +576,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_RED_BALANCE, @@ -574,6 +588,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 16, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_SIGNED, + .master_id = V4L2_CID_AUTO_WHITE_BALANCE, + .master_manual = 0, }, { .id = V4L2_CID_FOCUS_ABSOLUTE, @@ -584,6 +600,8 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_INTEGER, .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, + .master_id = V4L2_CID_FOCUS_AUTO, + .master_manual = 0, }, { .id = V4L2_CID_FOCUS_AUTO, @@ -594,6 +612,7 @@ static struct uvc_control_mapping uvc_ctrl_mappings[] = { .offset = 0, .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, + .slave_ids = { V4L2_CID_FOCUS_ABSOLUTE, }, }, { .id = V4L2_CID_IRIS_ABSOLUTE, @@ -899,25 +918,54 @@ static int uvc_ctrl_populate_cache(struct uvc_video_chain *chain, return 0; } -int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, - struct v4l2_queryctrl *v4l2_ctrl) +static int __uvc_ctrl_get(struct uvc_video_chain *chain, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 *value) { - struct uvc_control *ctrl; - struct uvc_control_mapping *mapping; struct uvc_menu_info *menu; unsigned int i; int ret; - ret = mutex_lock_interruptible(&chain->ctrl_mutex); - if (ret < 0) - return -ERESTARTSYS; + if ((ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) + return -EINVAL; - ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); - if (ctrl == NULL) { - ret = -EINVAL; - goto done; + if (!ctrl->loaded) { + ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, + chain->dev->intfnum, ctrl->info.selector, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), + ctrl->info.size); + if (ret < 0) + return ret; + + ctrl->loaded = 1; } + *value = mapping->get(mapping, UVC_GET_CUR, + uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); + + if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { + menu = mapping->menu_info; + for (i = 0; i < mapping->menu_count; ++i, ++menu) { + if (menu->value == *value) { + *value = i; + break; + } + } + } + + return 0; +} + +static int __uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control_mapping *master_map = NULL; + struct uvc_control *master_ctrl = NULL; + struct uvc_menu_info *menu; + unsigned int i; + memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl); v4l2_ctrl->id = mapping->id; v4l2_ctrl->type = mapping->v4l2_type; @@ -929,10 +977,23 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, if (!(ctrl->info.flags & UVC_CTRL_FLAG_SET_CUR)) v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; - if (!ctrl->cached) { - ret = uvc_ctrl_populate_cache(chain, ctrl); + if (mapping->master_id) + __uvc_find_control(ctrl->entity, mapping->master_id, + &master_map, &master_ctrl, 0); + if (master_ctrl && (master_ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) { + s32 val; + int ret = __uvc_ctrl_get(chain, master_ctrl, master_map, &val); if (ret < 0) - goto done; + return ret; + + if (val != mapping->master_manual) + v4l2_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE; + } + + if (!ctrl->cached) { + int ret = uvc_ctrl_populate_cache(chain, ctrl); + if (ret < 0) + return ret; } if (ctrl->info.flags & UVC_CTRL_FLAG_GET_DEF) { @@ -954,19 +1015,19 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, } } - goto done; + return 0; case V4L2_CTRL_TYPE_BOOLEAN: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = 1; v4l2_ctrl->step = 1; - goto done; + return 0; case V4L2_CTRL_TYPE_BUTTON: v4l2_ctrl->minimum = 0; v4l2_ctrl->maximum = 0; v4l2_ctrl->step = 0; - goto done; + return 0; default: break; @@ -984,6 +1045,27 @@ int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, v4l2_ctrl->step = mapping->get(mapping, UVC_GET_RES, uvc_ctrl_data(ctrl, UVC_CTRL_DATA_RES)); + return 0; +} + +int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, + struct v4l2_queryctrl *v4l2_ctrl) +{ + struct uvc_control *ctrl; + struct uvc_control_mapping *mapping; + int ret; + + ret = mutex_lock_interruptible(&chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(chain, v4l2_ctrl->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + ret = __uvc_query_v4l2_ctrl(chain, ctrl, mapping, v4l2_ctrl); done: mutex_unlock(&chain->ctrl_mutex); return ret; @@ -1054,6 +1136,174 @@ done: return ret; } +/* -------------------------------------------------------------------------- + * Ctrl event handling + */ + +static void uvc_ctrl_fill_event(struct uvc_video_chain *chain, + struct v4l2_event *ev, + struct uvc_control *ctrl, + struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_queryctrl v4l2_ctrl; + + __uvc_query_v4l2_ctrl(chain, ctrl, mapping, &v4l2_ctrl); + + memset(ev->reserved, 0, sizeof(ev->reserved)); + ev->type = V4L2_EVENT_CTRL; + ev->id = v4l2_ctrl.id; + ev->u.ctrl.value = value; + ev->u.ctrl.changes = changes; + ev->u.ctrl.type = v4l2_ctrl.type; + ev->u.ctrl.flags = v4l2_ctrl.flags; + ev->u.ctrl.minimum = v4l2_ctrl.minimum; + ev->u.ctrl.maximum = v4l2_ctrl.maximum; + ev->u.ctrl.step = v4l2_ctrl.step; + ev->u.ctrl.default_value = v4l2_ctrl.default_value; +} + +static void uvc_ctrl_send_event(struct uvc_fh *handle, + struct uvc_control *ctrl, struct uvc_control_mapping *mapping, + s32 value, u32 changes) +{ + struct v4l2_subscribed_event *sev; + struct v4l2_event ev; + + if (list_empty(&mapping->ev_subs)) + return; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, value, changes); + + list_for_each_entry(sev, &mapping->ev_subs, node) { + if (sev->fh && (sev->fh != &handle->vfh || + (sev->flags & V4L2_EVENT_SUB_FL_ALLOW_FEEDBACK) || + (changes & V4L2_EVENT_CTRL_CH_FLAGS))) + v4l2_event_queue_fh(sev->fh, &ev); + } +} + +static void uvc_ctrl_send_slave_event(struct uvc_fh *handle, + struct uvc_control *master, u32 slave_id, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping = NULL; + struct uvc_control *ctrl = NULL; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + unsigned int i; + s32 val = 0; + + /* + * We can skip sending an event for the slave if the slave + * is being modified in the same transaction. + */ + for (i = 0; i < xctrls_count; i++) { + if (xctrls[i].id == slave_id) + return; + } + + __uvc_find_control(master->entity, slave_id, &mapping, &ctrl, 0); + if (ctrl == NULL) + return; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_send_event(handle, ctrl, mapping, val, changes); +} + +static void uvc_ctrl_send_events(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, unsigned int xctrls_count) +{ + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + u32 changes = V4L2_EVENT_CTRL_CH_VALUE; + unsigned int i; + unsigned int j; + + for (i = 0; i < xctrls_count; ++i) { + ctrl = uvc_find_control(handle->chain, xctrls[i].id, &mapping); + + for (j = 0; j < ARRAY_SIZE(mapping->slave_ids); ++j) { + if (!mapping->slave_ids[j]) + break; + uvc_ctrl_send_slave_event(handle, ctrl, + mapping->slave_ids[j], + xctrls, xctrls_count); + } + + /* + * If the master is being modified in the same transaction + * flags may change too. + */ + if (mapping->master_id) { + for (j = 0; j < xctrls_count; j++) { + if (xctrls[j].id == mapping->master_id) { + changes |= V4L2_EVENT_CTRL_CH_FLAGS; + break; + } + } + } + + uvc_ctrl_send_event(handle, ctrl, mapping, xctrls[i].value, + changes); + } +} + +static int uvc_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + struct uvc_control_mapping *mapping; + struct uvc_control *ctrl; + int ret; + + ret = mutex_lock_interruptible(&handle->chain->ctrl_mutex); + if (ret < 0) + return -ERESTARTSYS; + + ctrl = uvc_find_control(handle->chain, sev->id, &mapping); + if (ctrl == NULL) { + ret = -EINVAL; + goto done; + } + + list_add_tail(&sev->node, &mapping->ev_subs); + if (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) { + struct v4l2_event ev; + u32 changes = V4L2_EVENT_CTRL_CH_FLAGS; + s32 val = 0; + + if (__uvc_ctrl_get(handle->chain, ctrl, mapping, &val) == 0) + changes |= V4L2_EVENT_CTRL_CH_VALUE; + + uvc_ctrl_fill_event(handle->chain, &ev, ctrl, mapping, val, + changes); + /* Mark the queue as active, allowing this initial + event to be accepted. */ + sev->elems = elems; + v4l2_event_queue_fh(sev->fh, &ev); + } + +done: + mutex_unlock(&handle->chain->ctrl_mutex); + return ret; +} + +static void uvc_ctrl_del_event(struct v4l2_subscribed_event *sev) +{ + struct uvc_fh *handle = container_of(sev->fh, struct uvc_fh, vfh); + + mutex_lock(&handle->chain->ctrl_mutex); + list_del(&sev->node); + mutex_unlock(&handle->chain->ctrl_mutex); +} + +const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops = { + .add = uvc_ctrl_add_event, + .del = uvc_ctrl_del_event, + .replace = v4l2_ctrl_replace, + .merge = v4l2_ctrl_merge, +}; /* -------------------------------------------------------------------------- * Control transactions @@ -1101,9 +1351,12 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, /* Reset the loaded flag for auto-update controls that were * marked as loaded in uvc_ctrl_get/uvc_ctrl_set to prevent - * uvc_ctrl_get from using the cached value. + * uvc_ctrl_get from using the cached value, and for write-only + * controls to prevent uvc_ctrl_set from setting bits not + * explicitly set by the user. */ - if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE) + if (ctrl->info.flags & UVC_CTRL_FLAG_AUTO_UPDATE || + !(ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR)) ctrl->loaded = 0; if (!ctrl->dirty) @@ -1131,8 +1384,11 @@ static int uvc_ctrl_commit_entity(struct uvc_device *dev, return 0; } -int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) +int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { + struct uvc_video_chain *chain = handle->chain; struct uvc_entity *entity; int ret = 0; @@ -1143,6 +1399,8 @@ int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback) goto done; } + if (!rollback) + uvc_ctrl_send_events(handle, xctrls, xctrls_count); done: mutex_unlock(&chain->ctrl_mutex); return ret; @@ -1153,39 +1411,12 @@ int uvc_ctrl_get(struct uvc_video_chain *chain, { struct uvc_control *ctrl; struct uvc_control_mapping *mapping; - struct uvc_menu_info *menu; - unsigned int i; - int ret; ctrl = uvc_find_control(chain, xctrl->id, &mapping); - if (ctrl == NULL || (ctrl->info.flags & UVC_CTRL_FLAG_GET_CUR) == 0) + if (ctrl == NULL) return -EINVAL; - if (!ctrl->loaded) { - ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, ctrl->entity->id, - chain->dev->intfnum, ctrl->info.selector, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), - ctrl->info.size); - if (ret < 0) - return ret; - - ctrl->loaded = 1; - } - - xctrl->value = mapping->get(mapping, UVC_GET_CUR, - uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT)); - - if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { - menu = mapping->menu_info; - for (i = 0; i < mapping->menu_count; ++i, ++menu) { - if (menu->value == xctrl->value) { - xctrl->value = i; - break; - } - } - } - - return 0; + return __uvc_ctrl_get(chain, ctrl, mapping, &xctrl->value); } int uvc_ctrl_set(struct uvc_video_chain *chain, @@ -1641,6 +1872,8 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, if (map == NULL) return -ENOMEM; + INIT_LIST_HEAD(&map->ev_subs); + size = sizeof(*mapping->menu_info) * mapping->menu_count; map->menu_info = kmemdup(mapping->menu_info, size, GFP_KERNEL); if (map->menu_info == NULL) { @@ -1653,7 +1886,6 @@ static int __uvc_ctrl_add_mapping(struct uvc_device *dev, if (map->set == NULL) map->set = uvc_set_le_value; - map->ctrl = &ctrl->info; list_add_tail(&map->list, &ctrl->info.mappings); uvc_trace(UVC_TRACE_CONTROL, "Adding mapping '%s' to control %pUl/%u.\n", diff --git a/drivers/media/video/uvc/uvc_queue.c b/drivers/media/video/uvc/uvc_queue.c index 8f54e24e3f35..9288fbd5001b 100644 --- a/drivers/media/video/uvc/uvc_queue.c +++ b/drivers/media/video/uvc/uvc_queue.c @@ -207,6 +207,19 @@ int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma) return ret; } +#ifndef CONFIG_MMU +unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, + unsigned long pgoff) +{ + unsigned long ret; + + mutex_lock(&queue->mutex); + ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0); + mutex_unlock(&queue->mutex); + return ret; +} +#endif + unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file, poll_table *wait) { @@ -237,36 +250,6 @@ int uvc_queue_allocated(struct uvc_video_queue *queue) return allocated; } -#ifndef CONFIG_MMU -/* - * Get unmapped area. - * - * NO-MMU arch need this function to make mmap() work correctly. - */ -unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue, - unsigned long pgoff) -{ - struct uvc_buffer *buffer; - unsigned int i; - unsigned long ret; - - mutex_lock(&queue->mutex); - for (i = 0; i < queue->count; ++i) { - buffer = &queue->buffer[i]; - if ((buffer->buf.m.offset >> PAGE_SHIFT) == pgoff) - break; - } - if (i == queue->count) { - ret = -EINVAL; - goto done; - } - ret = (unsigned long)buf->mem; -done: - mutex_unlock(&queue->mutex); - return ret; -} -#endif - /* * Enable or disable the video buffers queue. * diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c index ff2cdddf9bc6..759bef8897e9 100644 --- a/drivers/media/video/uvc/uvc_v4l2.c +++ b/drivers/media/video/uvc/uvc_v4l2.c @@ -25,6 +25,8 @@ #include #include +#include +#include #include #include "uvcvideo.h" @@ -505,6 +507,8 @@ static int uvc_v4l2_open(struct file *file) } } + v4l2_fh_init(&handle->vfh, stream->vdev); + v4l2_fh_add(&handle->vfh); handle->chain = stream->chain; handle->stream = stream; handle->state = UVC_HANDLE_PASSIVE; @@ -528,6 +532,8 @@ static int uvc_v4l2_release(struct file *file) /* Release the file handle. */ uvc_dismiss_privileges(handle); + v4l2_fh_del(&handle->vfh); + v4l2_fh_exit(&handle->vfh); kfree(handle); file->private_data = NULL; @@ -584,7 +590,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) return ret; ret = uvc_ctrl_get(chain, &xctrl); - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); if (ret >= 0) ctrl->value = xctrl.value; break; @@ -605,10 +611,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ret = uvc_ctrl_set(chain, &xctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); return ret; } - ret = uvc_ctrl_commit(chain); + ret = uvc_ctrl_commit(handle, &xctrl, 1); if (ret == 0) ctrl->value = xctrl.value; break; @@ -630,13 +636,13 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_get(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); ctrls->error_idx = i; return ret; } } ctrls->error_idx = 0; - ret = uvc_ctrl_rollback(chain); + ret = uvc_ctrl_rollback(handle); break; } @@ -654,7 +660,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_set(chain, ctrl); if (ret < 0) { - uvc_ctrl_rollback(chain); + uvc_ctrl_rollback(handle); ctrls->error_idx = i; return ret; } @@ -663,9 +669,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ctrls->error_idx = 0; if (cmd == VIDIOC_S_EXT_CTRLS) - ret = uvc_ctrl_commit(chain); + ret = uvc_ctrl_commit(handle, + ctrls->controls, ctrls->count); else - ret = uvc_ctrl_rollback(chain); + ret = uvc_ctrl_rollback(handle); break; } @@ -687,7 +694,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) break; } pin = iterm->id; - } else if (pin < selector->bNrInPins) { + } else if (index < selector->bNrInPins) { pin = selector->baSourceID[index]; list_for_each_entry(iterm, &chain->entities, chain) { if (!UVC_ENTITY_IS_ITERM(iterm)) @@ -990,6 +997,26 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) return uvc_video_enable(stream, 0); } + case VIDIOC_SUBSCRIBE_EVENT: + { + struct v4l2_event_subscription *sub = arg; + + switch (sub->type) { + case V4L2_EVENT_CTRL: + return v4l2_event_subscribe(&handle->vfh, sub, 0, + &uvc_ctrl_sub_ev_ops); + default: + return -EINVAL; + } + } + + case VIDIOC_UNSUBSCRIBE_EVENT: + return v4l2_event_unsubscribe(&handle->vfh, arg); + + case VIDIOC_DQEVENT: + return v4l2_event_dequeue(&handle->vfh, arg, + file->f_flags & O_NONBLOCK); + /* Analog video standards make no sense for digital cameras. */ case VIDIOC_ENUMSTD: case VIDIOC_QUERYSTD: @@ -1097,7 +1124,8 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, __put_user(kp->menu_count, &up->menu_count)) return -EFAULT; - __clear_user(up->reserved, sizeof(up->reserved)); + if (__clear_user(up->reserved, sizeof(up->reserved))) + return -EFAULT; if (kp->menu_count == 0) return 0; @@ -1105,8 +1133,6 @@ static int uvc_v4l2_put_xu_mapping(const struct uvc_xu_control_mapping *kp, if (get_user(p, &up->menu_info)) return -EFAULT; umenus = compat_ptr(p); - if (!access_ok(VERIFY_WRITE, umenus, kp->menu_count * sizeof(*umenus))) - return -EFAULT; if (copy_in_user(umenus, kmenus, kp->menu_count * sizeof(*umenus))) return -EFAULT; diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h index 67f88d85bb16..7c3d082505b7 100644 --- a/drivers/media/video/uvc/uvcvideo.h +++ b/drivers/media/video/uvc/uvcvideo.h @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include /* -------------------------------------------------------------------------- @@ -153,8 +155,7 @@ struct uvc_control_info { struct uvc_control_mapping { struct list_head list; - - struct uvc_control_info *ctrl; + struct list_head ev_subs; __u32 id; __u8 name[32]; @@ -169,6 +170,10 @@ struct uvc_control_mapping { struct uvc_menu_info *menu_info; __u32 menu_count; + __u32 master_id; + __s32 master_manual; + __u32 slave_ids[2]; + __s32 (*get) (struct uvc_control_mapping *mapping, __u8 query, const __u8 *data); void (*set) (struct uvc_control_mapping *mapping, __s32 value, @@ -524,6 +529,7 @@ enum uvc_handle_state { }; struct uvc_fh { + struct v4l2_fh vfh; struct uvc_video_chain *chain; struct uvc_streaming *stream; enum uvc_handle_state state; @@ -643,6 +649,8 @@ extern int uvc_status_suspend(struct uvc_device *dev); extern int uvc_status_resume(struct uvc_device *dev); /* Controls */ +extern const struct v4l2_subscribed_event_ops uvc_ctrl_sub_ev_ops; + extern int uvc_query_v4l2_ctrl(struct uvc_video_chain *chain, struct v4l2_queryctrl *v4l2_ctrl); extern int uvc_query_v4l2_menu(struct uvc_video_chain *chain, @@ -655,14 +663,18 @@ extern void uvc_ctrl_cleanup_device(struct uvc_device *dev); extern int uvc_ctrl_resume_device(struct uvc_device *dev); extern int uvc_ctrl_begin(struct uvc_video_chain *chain); -extern int __uvc_ctrl_commit(struct uvc_video_chain *chain, int rollback); -static inline int uvc_ctrl_commit(struct uvc_video_chain *chain) +extern int __uvc_ctrl_commit(struct uvc_fh *handle, int rollback, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count); +static inline int uvc_ctrl_commit(struct uvc_fh *handle, + const struct v4l2_ext_control *xctrls, + unsigned int xctrls_count) { - return __uvc_ctrl_commit(chain, 0); + return __uvc_ctrl_commit(handle, 0, xctrls, xctrls_count); } -static inline int uvc_ctrl_rollback(struct uvc_video_chain *chain) +static inline int uvc_ctrl_rollback(struct uvc_fh *handle) { - return __uvc_ctrl_commit(chain, 1); + return __uvc_ctrl_commit(handle, 1, NULL, 0); } extern int uvc_ctrl_get(struct uvc_video_chain *chain, diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c index 2829d256e4b7..5327ad3a6390 100644 --- a/drivers/media/video/v4l2-compat-ioctl32.c +++ b/drivers/media/video/v4l2-compat-ioctl32.c @@ -37,7 +37,7 @@ struct v4l2_clip32 { struct v4l2_window32 { struct v4l2_rect w; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ __u32 chromakey; compat_caddr_t clips; /* actually struct v4l2_clip32 * */ __u32 clipcount; @@ -147,7 +147,7 @@ static inline int put_v4l2_sliced_vbi_format(struct v4l2_sliced_vbi_format *kp, } struct v4l2_format32 { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ union { struct v4l2_pix_format pix; struct v4l2_pix_format_mplane pix_mp; @@ -170,7 +170,7 @@ struct v4l2_format32 { struct v4l2_create_buffers32 { __u32 index; __u32 count; - enum v4l2_memory memory; + __u32 memory; /* enum v4l2_memory */ struct v4l2_format32 format; __u32 reserved[8]; }; @@ -311,16 +311,16 @@ struct v4l2_plane32 { struct v4l2_buffer32 { __u32 index; - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ __u32 bytesused; __u32 flags; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ struct compat_timeval timestamp; struct v4l2_timecode timecode; __u32 sequence; /* memory location */ - enum v4l2_memory memory; + __u32 memory; /* enum v4l2_memory */ union { __u32 offset; compat_long_t userptr; @@ -1023,6 +1023,9 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg) case VIDIOC_UNSUBSCRIBE_EVENT: case VIDIOC_CREATE_BUFS32: case VIDIOC_PREPARE_BUF32: + case VIDIOC_ENUM_DV_TIMINGS: + case VIDIOC_QUERY_DV_TIMINGS: + case VIDIOC_DV_TIMINGS_CAP: ret = do_video_ioctl(file, cmd, arg); break; diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index 18015c0a8d31..9abd9abd4502 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c @@ -230,6 +230,19 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Aperture Priority Mode", NULL }; + static const char * const camera_exposure_metering[] = { + "Average", + "Center Weighted", + "Spot", + NULL + }; + static const char * const camera_auto_focus_range[] = { + "Auto", + "Normal", + "Macro", + "Infinity", + NULL + }; static const char * const colorfx[] = { "None", "Black & White", @@ -241,6 +254,47 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "Grass Green", "Skin Whiten", "Vivid", + "Aqua", + "Art Freeze", + "Silhouette", + "Solarization", + "Antique", + "Set Cb/Cr", + NULL + }; + static const char * const auto_n_preset_white_balance[] = { + "Manual", + "Auto", + "Incandescent", + "Fluorescent", + "Fluorescent H", + "Horizon", + "Daylight", + "Flash", + "Cloudy", + "Shade", + NULL, + }; + static const char * const camera_iso_sensitivity_auto[] = { + "Manual", + "Auto", + NULL + }; + static const char * const scene_mode[] = { + "None", + "Backlight", + "Beach/Snow", + "Candle Light", + "Dusk/Dawn", + "Fall Colors", + "Fireworks", + "Landscape", + "Night", + "Party/Indoor", + "Portrait", + "Sports", + "Sunset", + "Text", NULL }; static const char * const tune_preemphasis[] = { @@ -410,8 +464,18 @@ const char * const *v4l2_ctrl_get_menu(u32 id) return camera_power_line_frequency; case V4L2_CID_EXPOSURE_AUTO: return camera_exposure_auto; + case V4L2_CID_EXPOSURE_METERING: + return camera_exposure_metering; + case V4L2_CID_AUTO_FOCUS_RANGE: + return camera_auto_focus_range; case V4L2_CID_COLORFX: return colorfx; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: + return auto_n_preset_white_balance; + case V4L2_CID_ISO_SENSITIVITY_AUTO: + return camera_iso_sensitivity_auto; + case V4L2_CID_SCENE_MODE: + return scene_mode; case V4L2_CID_TUNE_PREEMPHASIS: return tune_preemphasis; case V4L2_CID_FLASH_LED_MODE: @@ -493,6 +557,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: return "Min Number of Capture Buffers"; case V4L2_CID_MIN_BUFFERS_FOR_OUTPUT: return "Min Number of Output Buffers"; case V4L2_CID_ALPHA_COMPONENT: return "Alpha Component"; + case V4L2_CID_COLORFX_CBCR: return "Color Effects, CbCr"; /* MPEG controls */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -590,13 +655,26 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_TILT_ABSOLUTE: return "Tilt, Absolute"; case V4L2_CID_FOCUS_ABSOLUTE: return "Focus, Absolute"; case V4L2_CID_FOCUS_RELATIVE: return "Focus, Relative"; - case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic"; + case V4L2_CID_FOCUS_AUTO: return "Focus, Automatic Continuous"; case V4L2_CID_ZOOM_ABSOLUTE: return "Zoom, Absolute"; case V4L2_CID_ZOOM_RELATIVE: return "Zoom, Relative"; case V4L2_CID_ZOOM_CONTINUOUS: return "Zoom, Continuous"; case V4L2_CID_PRIVACY: return "Privacy"; case V4L2_CID_IRIS_ABSOLUTE: return "Iris, Absolute"; case V4L2_CID_IRIS_RELATIVE: return "Iris, Relative"; + case V4L2_CID_AUTO_EXPOSURE_BIAS: return "Auto Exposure, Bias"; + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: return "White Balance, Auto & Preset"; + case V4L2_CID_WIDE_DYNAMIC_RANGE: return "Wide Dynamic Range"; + case V4L2_CID_IMAGE_STABILIZATION: return "Image Stabilization"; + case V4L2_CID_ISO_SENSITIVITY: return "ISO Sensitivity"; + case V4L2_CID_ISO_SENSITIVITY_AUTO: return "ISO Sensitivity, Auto"; + case V4L2_CID_EXPOSURE_METERING: return "Exposure, Metering Mode"; + case V4L2_CID_SCENE_MODE: return "Scene Mode"; + case V4L2_CID_3A_LOCK: return "3A Lock"; + case V4L2_CID_AUTO_FOCUS_START: return "Auto Focus, Start"; + case V4L2_CID_AUTO_FOCUS_STOP: return "Auto Focus, Stop"; + case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status"; + case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; /* FM Radio Modulator control */ /* Keep the order of the 'case's the same as in videodev2.h! */ @@ -644,6 +722,17 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_JPEG_COMPRESSION_QUALITY: return "Compression Quality"; case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; + /* Image source controls */ + case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls"; + case V4L2_CID_VBLANK: return "Vertical Blanking"; + case V4L2_CID_HBLANK: return "Horizontal Blanking"; + case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain"; + + /* Image processing controls */ + case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls"; + case V4L2_CID_LINK_FREQ: return "Link Frequency"; + case V4L2_CID_PIXEL_RATE: return "Pixel Rate"; + default: return NULL; } @@ -688,6 +777,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_H264_8X8_TRANSFORM: case V4L2_CID_MPEG_VIDEO_H264_VUI_SAR_ENABLE: case V4L2_CID_MPEG_VIDEO_MPEG4_QPEL: + case V4L2_CID_WIDE_DYNAMIC_RANGE: + case V4L2_CID_IMAGE_STABILIZATION: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -696,6 +787,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TILT_RESET: case V4L2_CID_FLASH_STROBE: case V4L2_CID_FLASH_STROBE_STOP: + case V4L2_CID_AUTO_FOCUS_START: + case V4L2_CID_AUTO_FOCUS_STOP: *type = V4L2_CTRL_TYPE_BUTTON; *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; *min = *max = *step = *def = 0; @@ -719,7 +812,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_STREAM_TYPE: case V4L2_CID_MPEG_STREAM_VBI_FMT: case V4L2_CID_EXPOSURE_AUTO: + case V4L2_CID_AUTO_FOCUS_RANGE: case V4L2_CID_COLORFX: + case V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE: case V4L2_CID_TUNE_PREEMPHASIS: case V4L2_CID_FLASH_LED_MODE: case V4L2_CID_FLASH_STROBE_SOURCE: @@ -733,18 +828,30 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_MPEG_VIDEO_MPEG4_LEVEL: case V4L2_CID_MPEG_VIDEO_MPEG4_PROFILE: case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: + case V4L2_CID_ISO_SENSITIVITY_AUTO: + case V4L2_CID_EXPOSURE_METERING: + case V4L2_CID_SCENE_MODE: *type = V4L2_CTRL_TYPE_MENU; break; + case V4L2_CID_LINK_FREQ: + *type = V4L2_CTRL_TYPE_INTEGER_MENU; + break; case V4L2_CID_RDS_TX_PS_NAME: case V4L2_CID_RDS_TX_RADIO_TEXT: *type = V4L2_CTRL_TYPE_STRING; break; + case V4L2_CID_ISO_SENSITIVITY: + case V4L2_CID_AUTO_EXPOSURE_BIAS: + *type = V4L2_CTRL_TYPE_INTEGER_MENU; + break; case V4L2_CID_USER_CLASS: case V4L2_CID_CAMERA_CLASS: case V4L2_CID_MPEG_CLASS: case V4L2_CID_FM_TX_CLASS: case V4L2_CID_FLASH_CLASS: case V4L2_CID_JPEG_CLASS: + case V4L2_CID_IMAGE_SOURCE_CLASS: + case V4L2_CID_IMAGE_PROC_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -759,6 +866,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_FLASH_FAULT: case V4L2_CID_JPEG_ACTIVE_MARKER: + case V4L2_CID_3A_LOCK: + case V4L2_CID_AUTO_FOCUS_STATUS: *type = V4L2_CTRL_TYPE_BITMASK; break; case V4L2_CID_MIN_BUFFERS_FOR_CAPTURE: @@ -768,8 +877,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_MPEG_VIDEO_DEC_FRAME: case V4L2_CID_MPEG_VIDEO_DEC_PTS: + *flags |= V4L2_CTRL_FLAG_VOLATILE; + /* Fall through */ + case V4L2_CID_PIXEL_RATE: *type = V4L2_CTRL_TYPE_INTEGER64; - *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_VOLATILE; + *flags |= V4L2_CTRL_FLAG_READ_ONLY; + *min = *max = *step = *def = 0; break; default: *type = V4L2_CTRL_TYPE_INTEGER; @@ -817,6 +930,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *flags |= V4L2_CTRL_FLAG_WRITE_ONLY; break; case V4L2_CID_FLASH_STROBE_STATUS: + case V4L2_CID_AUTO_FOCUS_STATUS: case V4L2_CID_FLASH_READY: *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; @@ -852,7 +966,8 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->u.ctrl.value64 = ctrl->cur.val64; ev->u.ctrl.minimum = ctrl->minimum; ev->u.ctrl.maximum = ctrl->maximum; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) ev->u.ctrl.step = 1; else ev->u.ctrl.step = ctrl->step; @@ -1083,10 +1198,13 @@ static int validate_new_int(const struct v4l2_ctrl *ctrl, s32 *pval) return 0; case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: if (val < ctrl->minimum || val > ctrl->maximum) return -ERANGE; - if (ctrl->qmenu[val][0] == '\0' || - (ctrl->menu_skip_mask & (1 << val))) + if (ctrl->menu_skip_mask & (1 << val)) + return -EINVAL; + if (ctrl->type == V4L2_CTRL_TYPE_MENU && + ctrl->qmenu[val][0] == '\0') return -EINVAL; return 0; @@ -1114,6 +1232,7 @@ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: @@ -1152,7 +1271,8 @@ static inline int handler_set_err(struct v4l2_ctrl_handler *hdl, int err) int v4l2_ctrl_handler_init(struct v4l2_ctrl_handler *hdl, unsigned nr_of_controls_hint) { - mutex_init(&hdl->lock); + hdl->lock = &hdl->_lock; + mutex_init(hdl->lock); INIT_LIST_HEAD(&hdl->ctrls); INIT_LIST_HEAD(&hdl->ctrl_refs); hdl->nr_of_buckets = 1 + nr_of_controls_hint / 8; @@ -1173,7 +1293,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) if (hdl == NULL || hdl->buckets == NULL) return; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Free all nodes */ list_for_each_entry_safe(ref, next_ref, &hdl->ctrl_refs, node) { list_del(&ref->node); @@ -1190,7 +1310,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl) hdl->buckets = NULL; hdl->cached = NULL; hdl->error = 0; - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } EXPORT_SYMBOL(v4l2_ctrl_handler_free); @@ -1255,9 +1375,9 @@ static struct v4l2_ctrl_ref *find_ref_lock( struct v4l2_ctrl_ref *ref = NULL; if (hdl) { - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); ref = find_ref(hdl, id); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } return ref; } @@ -1304,7 +1424,7 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, INIT_LIST_HEAD(&new_ref->node); - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Add immediately at the end of the list if the list is empty, or if the last element in the list has a lower ID. @@ -1334,7 +1454,7 @@ insert_in_hash: hdl->buckets[bucket] = new_ref; unlock: - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return 0; } @@ -1343,7 +1463,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, s32 min, s32 max, u32 step, s32 def, - u32 flags, const char * const *qmenu, void *priv) + u32 flags, const char * const *qmenu, + const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra = 0; @@ -1356,6 +1477,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, (type == V4L2_CTRL_TYPE_INTEGER && step == 0) || (type == V4L2_CTRL_TYPE_BITMASK && max == 0) || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || + (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL) || (type == V4L2_CTRL_TYPE_STRING && max == 0)) { handler_set_err(hdl, -ERANGE); return NULL; @@ -1366,6 +1488,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } if ((type == V4L2_CTRL_TYPE_INTEGER || type == V4L2_CTRL_TYPE_MENU || + type == V4L2_CTRL_TYPE_INTEGER_MENU || type == V4L2_CTRL_TYPE_BOOLEAN) && (def < min || def > max)) { handler_set_err(hdl, -ERANGE); @@ -1400,7 +1523,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; - ctrl->qmenu = qmenu; + if (type == V4L2_CTRL_TYPE_MENU) + ctrl->qmenu = qmenu; + else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) + ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; ctrl->cur.val = ctrl->val = ctrl->default_value = def; @@ -1414,9 +1540,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, kfree(ctrl); return NULL; } - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_add_tail(&ctrl->node, &hdl->ctrls); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return ctrl; } @@ -1427,6 +1553,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl; const char *name = cfg->name; const char * const *qmenu = cfg->qmenu; + const s64 *qmenu_int = cfg->qmenu_int; enum v4l2_ctrl_type type = cfg->type; u32 flags = cfg->flags; s32 min = cfg->min; @@ -1438,18 +1565,24 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, &def, &flags); - is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU); + is_menu = (cfg->type == V4L2_CTRL_TYPE_MENU || + cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU); if (is_menu) WARN_ON(step); else WARN_ON(cfg->menu_skip_mask); - if (is_menu && qmenu == NULL) + if (cfg->type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) qmenu = v4l2_ctrl_get_menu(cfg->id); + else if (cfg->type == V4L2_CTRL_TYPE_INTEGER_MENU && + qmenu_int == NULL) { + handler_set_err(hdl, -EINVAL); + return NULL; + } ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, priv); + def, flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -1466,12 +1599,13 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU) { + if (type == V4L2_CTRL_TYPE_MENU + || type == V4L2_CTRL_TYPE_INTEGER_MENU) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL); + min, max, step, def, flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1493,10 +1627,31 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, NULL); + 0, max, mask, def, flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); +/* Helper function for standard integer menu controls */ +struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 max, s32 def, const s64 *qmenu_int) +{ + const char *name; + enum v4l2_ctrl_type type; + s32 min; + s32 step; + u32 flags; + + v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); + if (type != V4L2_CTRL_TYPE_INTEGER_MENU) { + handler_set_err(hdl, -EINVAL); + return NULL; + } + return v4l2_ctrl_new(hdl, ops, id, name, type, + 0, max, 0, def, flags, NULL, qmenu_int, NULL); +} +EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); + /* Add a control from another handler to this handler */ struct v4l2_ctrl *v4l2_ctrl_add_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_ctrl *ctrl) @@ -1525,7 +1680,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, return 0; if (hdl->error) return hdl->error; - mutex_lock(&add->lock); + mutex_lock(add->lock); list_for_each_entry(ref, &add->ctrl_refs, node) { struct v4l2_ctrl *ctrl = ref->ctrl; @@ -1539,7 +1694,7 @@ int v4l2_ctrl_add_handler(struct v4l2_ctrl_handler *hdl, if (ret) break; } - mutex_unlock(&add->lock); + mutex_unlock(add->lock); return ret; } EXPORT_SYMBOL(v4l2_ctrl_add_handler); @@ -1659,6 +1814,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); + break; case V4L2_CTRL_TYPE_BITMASK: printk(KERN_CONT "0x%08x", ctrl->cur.val); break; @@ -1700,11 +1858,11 @@ void v4l2_ctrl_handler_log_status(struct v4l2_ctrl_handler *hdl, len = strlen(prefix); if (len && prefix[len - 1] != ' ') colon = ": "; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_for_each_entry(ctrl, &hdl->ctrls, node) if (!(ctrl->flags & V4L2_CTRL_FLAG_DISABLED)) log_ctrl(ctrl, prefix, colon); - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); } EXPORT_SYMBOL(v4l2_ctrl_handler_log_status); @@ -1716,7 +1874,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) if (hdl == NULL) return 0; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); list_for_each_entry(ctrl, &hdl->ctrls, node) ctrl->done = false; @@ -1741,7 +1899,7 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) if (ret) break; } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return ret; } EXPORT_SYMBOL(v4l2_ctrl_handler_setup); @@ -1756,7 +1914,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) if (hdl == NULL) return -EINVAL; - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* Try to find it */ ref = find_ref(hdl, id); @@ -1781,7 +1939,7 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) break; } } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); if (!ref) return -EINVAL; @@ -1795,7 +1953,8 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; - if (ctrl->type == V4L2_CTRL_TYPE_MENU) + if (ctrl->type == V4L2_CTRL_TYPE_MENU + || ctrl->type == V4L2_CTRL_TYPE_INTEGER_MENU) qc->step = 1; else qc->step = ctrl->step; @@ -1825,16 +1984,33 @@ int v4l2_querymenu(struct v4l2_ctrl_handler *hdl, struct v4l2_querymenu *qm) qm->reserved = 0; /* Sanity checks */ - if (ctrl->qmenu == NULL || - i < ctrl->minimum || i > ctrl->maximum) + switch (ctrl->type) { + case V4L2_CTRL_TYPE_MENU: + if (ctrl->qmenu == NULL) + return -EINVAL; + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (ctrl->qmenu_int == NULL) + return -EINVAL; + break; + default: return -EINVAL; + } + + if (i < ctrl->minimum || i > ctrl->maximum) + return -EINVAL; + /* Use mask to see if this menu item should be skipped */ if (ctrl->menu_skip_mask & (1 << i)) return -EINVAL; /* Empty menu items should also be skipped */ - if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') - return -EINVAL; - strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + if (ctrl->type == V4L2_CTRL_TYPE_MENU) { + if (ctrl->qmenu[i] == NULL || ctrl->qmenu[i][0] == '\0') + return -EINVAL; + strlcpy(qm->name, ctrl->qmenu[i], sizeof(qm->name)); + } else { + qm->value = ctrl->qmenu_int[i]; + } return 0; } EXPORT_SYMBOL(v4l2_querymenu); @@ -1940,7 +2116,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, belong to the same cluster. */ /* This has to be done with the handler lock taken. */ - mutex_lock(&hdl->lock); + mutex_lock(hdl->lock); /* First zero the helper field in the master control references */ for (i = 0; i < cs->count; i++) @@ -1962,7 +2138,7 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, /* Point the mref helper to the current helper struct. */ mref->helper = h; } - mutex_unlock(&hdl->lock); + mutex_unlock(hdl->lock); return 0; } @@ -1996,7 +2172,8 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs return class_check(hdl, cs->ctrl_class); if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + helpers = kmalloc_array(cs->count, sizeof(helper[0]), + GFP_KERNEL); if (helpers == NULL) return -ENOMEM; } @@ -2218,7 +2395,8 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, return class_check(hdl, cs->ctrl_class); if (cs->count > ARRAY_SIZE(helper)) { - helpers = kmalloc(sizeof(helper[0]) * cs->count, GFP_KERNEL); + helpers = kmalloc_array(cs->count, sizeof(helper[0]), + GFP_KERNEL); if (!helpers) return -ENOMEM; } @@ -2381,9 +2559,13 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) } EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); -void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev) +static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); + + if (ctrl == NULL) + return -EINVAL; + v4l2_ctrl_lock(ctrl); list_add_tail(&sev->node, &ctrl->ev_subs); if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS && @@ -2394,20 +2576,46 @@ void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, if (!(ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)) changes |= V4L2_EVENT_CTRL_CH_VALUE; fill_event(&ev, ctrl, changes); + /* Mark the queue as active, allowing this initial + event to be accepted. */ + sev->elems = elems; v4l2_event_queue_fh(sev->fh, &ev); } v4l2_ctrl_unlock(ctrl); + return 0; } -EXPORT_SYMBOL(v4l2_ctrl_add_event); -void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev) +static void v4l2_ctrl_del_event(struct v4l2_subscribed_event *sev) { + struct v4l2_ctrl *ctrl = v4l2_ctrl_find(sev->fh->ctrl_handler, sev->id); + v4l2_ctrl_lock(ctrl); list_del(&sev->node); v4l2_ctrl_unlock(ctrl); } -EXPORT_SYMBOL(v4l2_ctrl_del_event); + +void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new) +{ + u32 old_changes = old->u.ctrl.changes; + + old->u.ctrl = new->u.ctrl; + old->u.ctrl.changes |= old_changes; +} +EXPORT_SYMBOL(v4l2_ctrl_replace); + +void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new) +{ + new->u.ctrl.changes |= old->u.ctrl.changes; +} +EXPORT_SYMBOL(v4l2_ctrl_merge); + +const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops = { + .add = v4l2_ctrl_add_event, + .del = v4l2_ctrl_del_event, + .replace = v4l2_ctrl_replace, + .merge = v4l2_ctrl_merge, +}; +EXPORT_SYMBOL(v4l2_ctrl_sub_ev_ops); int v4l2_ctrl_log_status(struct file *file, void *fh) { @@ -2425,7 +2633,7 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscription *sub) { if (sub->type == V4L2_EVENT_CTRL) - return v4l2_event_subscribe(fh, sub, 0); + return v4l2_event_subscribe(fh, sub, 0, &v4l2_ctrl_sub_ev_ops); return -EINVAL; } EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c index 70bec548d904..5ccbd4629f9c 100644 --- a/drivers/media/video/v4l2-dev.c +++ b/drivers/media/video/v4l2-dev.c @@ -274,11 +274,12 @@ static ssize_t v4l2_read(struct file *filp, char __user *buf, if (!vdev->fops->read) return -EINVAL; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->read(filp, buf, sz, off); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -291,11 +292,12 @@ static ssize_t v4l2_write(struct file *filp, const char __user *buf, if (!vdev->fops->write) return -EINVAL; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->write(filp, buf, sz, off); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -307,11 +309,11 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) if (!vdev->fops->poll) return DEFAULT_POLLMASK; - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_lock(vdev->lock); if (video_is_registered(vdev)) ret = vdev->fops->poll(filp, poll); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -322,11 +324,19 @@ static long v4l2_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) int ret = -ENODEV; if (vdev->fops->unlocked_ioctl) { - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; + bool locked = false; + + if (vdev->lock) { + /* always lock unless the cmd is marked as "don't use lock" */ + locked = !v4l2_is_known_ioctl(cmd) || + !test_bit(_IOC_NR(cmd), vdev->disable_locking); + + if (locked && mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + } if (video_is_registered(vdev)) ret = vdev->fops->unlocked_ioctl(filp, cmd, arg); - if (vdev->lock) + if (locked) mutex_unlock(vdev->lock); } else if (vdev->fops->ioctl) { /* This code path is a replacement for the BKL. It is a major @@ -391,11 +401,12 @@ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm) if (!vdev->fops->mmap) return ret; - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) return -ERESTARTSYS; if (video_is_registered(vdev)) ret = vdev->fops->mmap(filp, vm); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); return ret; } @@ -418,7 +429,8 @@ static int v4l2_open(struct inode *inode, struct file *filp) video_get(vdev); mutex_unlock(&videodev_lock); if (vdev->fops->open) { - if (vdev->lock && mutex_lock_interruptible(vdev->lock)) { + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags) && + mutex_lock_interruptible(vdev->lock)) { ret = -ERESTARTSYS; goto err; } @@ -426,7 +438,7 @@ static int v4l2_open(struct inode *inode, struct file *filp) ret = vdev->fops->open(filp); else ret = -ENODEV; - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); } @@ -444,10 +456,10 @@ static int v4l2_release(struct inode *inode, struct file *filp) int ret = 0; if (vdev->fops->release) { - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_lock(vdev->lock); vdev->fops->release(filp); - if (vdev->lock) + if (test_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)) mutex_unlock(vdev->lock); } /* decrease the refcount unconditionally since the release() @@ -508,6 +520,175 @@ static int get_index(struct video_device *vdev) return find_first_zero_bit(used, VIDEO_NUM_DEVICES); } +#define SET_VALID_IOCTL(ops, cmd, op) \ + if (ops->op) \ + set_bit(_IOC_NR(cmd), valid_ioctls) + +/* This determines which ioctls are actually implemented in the driver. + It's a one-time thing which simplifies video_ioctl2 as it can just do + a bit test. + + Note that drivers can override this by setting bits to 1 in + vdev->valid_ioctls. If an ioctl is marked as 1 when this function is + called, then that ioctl will actually be marked as unimplemented. + + It does that by first setting up the local valid_ioctls bitmap, and + at the end do a: + + vdev->valid_ioctls = valid_ioctls & ~(vdev->valid_ioctls) + */ +static void determine_valid_ioctls(struct video_device *vdev) +{ + DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); + const struct v4l2_ioctl_ops *ops = vdev->ioctl_ops; + + bitmap_zero(valid_ioctls, BASE_VIDIOC_PRIVATE); + + SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap); + if (ops->vidioc_g_priority || + test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); + if (ops->vidioc_s_priority || + test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); + if (ops->vidioc_enum_fmt_vid_cap || + ops->vidioc_enum_fmt_vid_out || + ops->vidioc_enum_fmt_vid_cap_mplane || + ops->vidioc_enum_fmt_vid_out_mplane || + ops->vidioc_enum_fmt_vid_overlay || + ops->vidioc_enum_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_ENUM_FMT), valid_ioctls); + if (ops->vidioc_g_fmt_vid_cap || + ops->vidioc_g_fmt_vid_out || + ops->vidioc_g_fmt_vid_cap_mplane || + ops->vidioc_g_fmt_vid_out_mplane || + ops->vidioc_g_fmt_vid_overlay || + ops->vidioc_g_fmt_vbi_cap || + ops->vidioc_g_fmt_vid_out_overlay || + ops->vidioc_g_fmt_vbi_out || + ops->vidioc_g_fmt_sliced_vbi_cap || + ops->vidioc_g_fmt_sliced_vbi_out || + ops->vidioc_g_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_G_FMT), valid_ioctls); + if (ops->vidioc_s_fmt_vid_cap || + ops->vidioc_s_fmt_vid_out || + ops->vidioc_s_fmt_vid_cap_mplane || + ops->vidioc_s_fmt_vid_out_mplane || + ops->vidioc_s_fmt_vid_overlay || + ops->vidioc_s_fmt_vbi_cap || + ops->vidioc_s_fmt_vid_out_overlay || + ops->vidioc_s_fmt_vbi_out || + ops->vidioc_s_fmt_sliced_vbi_cap || + ops->vidioc_s_fmt_sliced_vbi_out || + ops->vidioc_s_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_S_FMT), valid_ioctls); + if (ops->vidioc_try_fmt_vid_cap || + ops->vidioc_try_fmt_vid_out || + ops->vidioc_try_fmt_vid_cap_mplane || + ops->vidioc_try_fmt_vid_out_mplane || + ops->vidioc_try_fmt_vid_overlay || + ops->vidioc_try_fmt_vbi_cap || + ops->vidioc_try_fmt_vid_out_overlay || + ops->vidioc_try_fmt_vbi_out || + ops->vidioc_try_fmt_sliced_vbi_cap || + ops->vidioc_try_fmt_sliced_vbi_out || + ops->vidioc_try_fmt_type_private) + set_bit(_IOC_NR(VIDIOC_TRY_FMT), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_REQBUFS, vidioc_reqbufs); + SET_VALID_IOCTL(ops, VIDIOC_QUERYBUF, vidioc_querybuf); + SET_VALID_IOCTL(ops, VIDIOC_QBUF, vidioc_qbuf); + SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); + SET_VALID_IOCTL(ops, VIDIOC_OVERLAY, vidioc_overlay); + SET_VALID_IOCTL(ops, VIDIOC_G_FBUF, vidioc_g_fbuf); + SET_VALID_IOCTL(ops, VIDIOC_S_FBUF, vidioc_s_fbuf); + SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); + SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); + if (vdev->tvnorms) + set_bit(_IOC_NR(VIDIOC_ENUMSTD), valid_ioctls); + if (ops->vidioc_g_std || vdev->current_norm) + set_bit(_IOC_NR(VIDIOC_G_STD), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_S_STD, vidioc_s_std); + SET_VALID_IOCTL(ops, VIDIOC_QUERYSTD, vidioc_querystd); + SET_VALID_IOCTL(ops, VIDIOC_ENUMINPUT, vidioc_enum_input); + SET_VALID_IOCTL(ops, VIDIOC_G_INPUT, vidioc_g_input); + SET_VALID_IOCTL(ops, VIDIOC_S_INPUT, vidioc_s_input); + SET_VALID_IOCTL(ops, VIDIOC_ENUMOUTPUT, vidioc_enum_output); + SET_VALID_IOCTL(ops, VIDIOC_G_OUTPUT, vidioc_g_output); + SET_VALID_IOCTL(ops, VIDIOC_S_OUTPUT, vidioc_s_output); + /* Note: the control handler can also be passed through the filehandle, + and that can't be tested here. If the bit for these control ioctls + is set, then the ioctl is valid. But if it is 0, then it can still + be valid if the filehandle passed the control handler. */ + if (vdev->ctrl_handler || ops->vidioc_queryctrl) + set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_S_CTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_g_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_G_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_s_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_S_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_try_ext_ctrls) + set_bit(_IOC_NR(VIDIOC_TRY_EXT_CTRLS), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_querymenu) + set_bit(_IOC_NR(VIDIOC_QUERYMENU), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDIO, vidioc_enumaudio); + SET_VALID_IOCTL(ops, VIDIOC_G_AUDIO, vidioc_g_audio); + SET_VALID_IOCTL(ops, VIDIOC_S_AUDIO, vidioc_s_audio); + SET_VALID_IOCTL(ops, VIDIOC_ENUMAUDOUT, vidioc_enumaudout); + SET_VALID_IOCTL(ops, VIDIOC_G_AUDOUT, vidioc_g_audout); + SET_VALID_IOCTL(ops, VIDIOC_S_AUDOUT, vidioc_s_audout); + SET_VALID_IOCTL(ops, VIDIOC_G_MODULATOR, vidioc_g_modulator); + SET_VALID_IOCTL(ops, VIDIOC_S_MODULATOR, vidioc_s_modulator); + if (ops->vidioc_g_crop || ops->vidioc_g_selection) + set_bit(_IOC_NR(VIDIOC_G_CROP), valid_ioctls); + if (ops->vidioc_s_crop || ops->vidioc_s_selection) + set_bit(_IOC_NR(VIDIOC_S_CROP), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_G_SELECTION, vidioc_g_selection); + SET_VALID_IOCTL(ops, VIDIOC_S_SELECTION, vidioc_s_selection); + if (ops->vidioc_cropcap || ops->vidioc_g_selection) + set_bit(_IOC_NR(VIDIOC_CROPCAP), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp); + SET_VALID_IOCTL(ops, VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp); + SET_VALID_IOCTL(ops, VIDIOC_G_ENC_INDEX, vidioc_g_enc_index); + SET_VALID_IOCTL(ops, VIDIOC_ENCODER_CMD, vidioc_encoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_DECODER_CMD, vidioc_decoder_cmd); + SET_VALID_IOCTL(ops, VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd); + if (ops->vidioc_g_parm || vdev->current_norm) + set_bit(_IOC_NR(VIDIOC_G_PARM), valid_ioctls); + SET_VALID_IOCTL(ops, VIDIOC_S_PARM, vidioc_s_parm); + SET_VALID_IOCTL(ops, VIDIOC_G_TUNER, vidioc_g_tuner); + SET_VALID_IOCTL(ops, VIDIOC_S_TUNER, vidioc_s_tuner); + SET_VALID_IOCTL(ops, VIDIOC_G_FREQUENCY, vidioc_g_frequency); + SET_VALID_IOCTL(ops, VIDIOC_S_FREQUENCY, vidioc_s_frequency); + SET_VALID_IOCTL(ops, VIDIOC_G_SLICED_VBI_CAP, vidioc_g_sliced_vbi_cap); + SET_VALID_IOCTL(ops, VIDIOC_LOG_STATUS, vidioc_log_status); +#ifdef CONFIG_VIDEO_ADV_DEBUG + SET_VALID_IOCTL(ops, VIDIOC_DBG_G_REGISTER, vidioc_g_register); + SET_VALID_IOCTL(ops, VIDIOC_DBG_S_REGISTER, vidioc_s_register); +#endif + SET_VALID_IOCTL(ops, VIDIOC_DBG_G_CHIP_IDENT, vidioc_g_chip_ident); + SET_VALID_IOCTL(ops, VIDIOC_S_HW_FREQ_SEEK, vidioc_s_hw_freq_seek); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals); + SET_VALID_IOCTL(ops, VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets); + SET_VALID_IOCTL(ops, VIDIOC_S_DV_PRESET, vidioc_s_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_G_DV_PRESET, vidioc_g_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset); + SET_VALID_IOCTL(ops, VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings); + SET_VALID_IOCTL(ops, VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings); + /* yes, really vidioc_subscribe_event */ + SET_VALID_IOCTL(ops, VIDIOC_DQEVENT, vidioc_subscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_SUBSCRIBE_EVENT, vidioc_subscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_UNSUBSCRIBE_EVENT, vidioc_unsubscribe_event); + SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); + SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); + bitmap_andnot(vdev->valid_ioctls, valid_ioctls, vdev->valid_ioctls, + BASE_VIDIOC_PRIVATE); +} + /** * __video_register_device - register video4linux devices * @vdev: video device structure we want to register @@ -654,6 +835,13 @@ int __video_register_device(struct video_device *vdev, int type, int nr, WARN_ON(video_device[vdev->minor] != NULL); vdev->index = get_index(vdev); mutex_unlock(&videodev_lock); + /* if no lock was passed, then make sure the LOCK_ALL_FOPS bit is + clear and warn if it wasn't. */ + if (vdev->lock == NULL) + WARN_ON(test_and_clear_bit(V4L2_FL_LOCK_ALL_FOPS, &vdev->flags)); + + if (vdev->ioctl_ops) + determine_valid_ioctls(vdev); /* Part 3: Initialize the character device */ vdev->cdev = cdev_alloc(); diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index c26ad9637143..ef2a33c94045 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c @@ -25,7 +25,6 @@ #include #include #include -#include #include #include @@ -120,6 +119,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e if (sev == NULL) return; + /* + * If the event has been added to the fh->subscribed list, but its + * add op has not completed yet elems will be 0, treat this as + * not being subscribed. + */ + if (!sev->elems) + return; + /* Increase event sequence number on fh. */ fh->sequence++; @@ -132,14 +139,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e sev->first = sev_pos(sev, 1); fh->navailable--; if (sev->elems == 1) { - if (sev->replace) { - sev->replace(&kev->event, ev); + if (sev->ops && sev->ops->replace) { + sev->ops->replace(&kev->event, ev); copy_payload = false; } - } else if (sev->merge) { + } else if (sev->ops && sev->ops->merge) { struct v4l2_kevent *second_oldest = sev->events + sev_pos(sev, 0); - sev->merge(&kev->event, &second_oldest->event); + sev->ops->merge(&kev->event, &second_oldest->event); } } @@ -195,24 +202,11 @@ int v4l2_event_pending(struct v4l2_fh *fh) } EXPORT_SYMBOL_GPL(v4l2_event_pending); -static void ctrls_replace(struct v4l2_event *old, const struct v4l2_event *new) -{ - u32 old_changes = old->u.ctrl.changes; - - old->u.ctrl = new->u.ctrl; - old->u.ctrl.changes |= old_changes; -} - -static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new) -{ - new->u.ctrl.changes |= old->u.ctrl.changes; -} - int v4l2_event_subscribe(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub, unsigned elems) + struct v4l2_event_subscription *sub, unsigned elems, + const struct v4l2_subscribed_event_ops *ops) { struct v4l2_subscribed_event *sev, *found_ev; - struct v4l2_ctrl *ctrl = NULL; unsigned long flags; unsigned i; @@ -221,11 +215,6 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, if (elems < 1) elems = 1; - if (sub->type == V4L2_EVENT_CTRL) { - ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id); - if (ctrl == NULL) - return -EINVAL; - } sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL); if (!sev) @@ -236,11 +225,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, sev->id = sub->id; sev->flags = sub->flags; sev->fh = fh; - sev->elems = elems; - if (ctrl) { - sev->replace = ctrls_replace; - sev->merge = ctrls_merge; - } + sev->ops = ops; spin_lock_irqsave(&fh->vdev->fh_lock, flags); found_ev = v4l2_event_subscribed(fh, sub->type, sub->id); @@ -248,11 +233,22 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, list_add(&sev->list, &fh->subscribed); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */ - if (found_ev) + if (found_ev) { kfree(sev); - else if (ctrl) - v4l2_ctrl_add_event(ctrl, sev); + return 0; /* Already listening */ + } + + if (sev->ops && sev->ops->add) { + int ret = sev->ops->add(sev, elems); + if (ret) { + sev->ops = NULL; + v4l2_event_unsubscribe(fh, sub); + return ret; + } + } + + /* Mark as ready for use */ + sev->elems = elems; return 0; } @@ -306,12 +302,9 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, } spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - if (sev && sev->type == V4L2_EVENT_CTRL) { - struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id); - if (ctrl) - v4l2_ctrl_del_event(ctrl, sev); - } + if (sev && sev->ops && sev->ops->del) + sev->ops->del(sev); kfree(sev); diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c index 5b2ec1fd2d0a..91be4e871f43 100644 --- a/drivers/media/video/v4l2-ioctl.c +++ b/drivers/media/video/v4l2-ioctl.c @@ -55,19 +55,6 @@ memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \ 0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field)) -#define have_fmt_ops(foo) ( \ - ops->vidioc_##foo##_fmt_vid_cap || \ - ops->vidioc_##foo##_fmt_vid_out || \ - ops->vidioc_##foo##_fmt_vid_cap_mplane || \ - ops->vidioc_##foo##_fmt_vid_out_mplane || \ - ops->vidioc_##foo##_fmt_vid_overlay || \ - ops->vidioc_##foo##_fmt_vbi_cap || \ - ops->vidioc_##foo##_fmt_vid_out_overlay || \ - ops->vidioc_##foo##_fmt_vbi_out || \ - ops->vidioc_##foo##_fmt_sliced_vbi_cap || \ - ops->vidioc_##foo##_fmt_sliced_vbi_out || \ - ops->vidioc_##foo##_fmt_type_private) - struct std_descr { v4l2_std_id std; const char *descr; @@ -195,93 +182,118 @@ static const char *v4l2_memory_names[] = { /* ------------------------------------------------------------------ */ /* debug help functions */ -static const char *v4l2_ioctls[] = { - [_IOC_NR(VIDIOC_QUERYCAP)] = "VIDIOC_QUERYCAP", - [_IOC_NR(VIDIOC_RESERVED)] = "VIDIOC_RESERVED", - [_IOC_NR(VIDIOC_ENUM_FMT)] = "VIDIOC_ENUM_FMT", - [_IOC_NR(VIDIOC_G_FMT)] = "VIDIOC_G_FMT", - [_IOC_NR(VIDIOC_S_FMT)] = "VIDIOC_S_FMT", - [_IOC_NR(VIDIOC_REQBUFS)] = "VIDIOC_REQBUFS", - [_IOC_NR(VIDIOC_QUERYBUF)] = "VIDIOC_QUERYBUF", - [_IOC_NR(VIDIOC_G_FBUF)] = "VIDIOC_G_FBUF", - [_IOC_NR(VIDIOC_S_FBUF)] = "VIDIOC_S_FBUF", - [_IOC_NR(VIDIOC_OVERLAY)] = "VIDIOC_OVERLAY", - [_IOC_NR(VIDIOC_QBUF)] = "VIDIOC_QBUF", - [_IOC_NR(VIDIOC_DQBUF)] = "VIDIOC_DQBUF", - [_IOC_NR(VIDIOC_STREAMON)] = "VIDIOC_STREAMON", - [_IOC_NR(VIDIOC_STREAMOFF)] = "VIDIOC_STREAMOFF", - [_IOC_NR(VIDIOC_G_PARM)] = "VIDIOC_G_PARM", - [_IOC_NR(VIDIOC_S_PARM)] = "VIDIOC_S_PARM", - [_IOC_NR(VIDIOC_G_STD)] = "VIDIOC_G_STD", - [_IOC_NR(VIDIOC_S_STD)] = "VIDIOC_S_STD", - [_IOC_NR(VIDIOC_ENUMSTD)] = "VIDIOC_ENUMSTD", - [_IOC_NR(VIDIOC_ENUMINPUT)] = "VIDIOC_ENUMINPUT", - [_IOC_NR(VIDIOC_G_CTRL)] = "VIDIOC_G_CTRL", - [_IOC_NR(VIDIOC_S_CTRL)] = "VIDIOC_S_CTRL", - [_IOC_NR(VIDIOC_G_TUNER)] = "VIDIOC_G_TUNER", - [_IOC_NR(VIDIOC_S_TUNER)] = "VIDIOC_S_TUNER", - [_IOC_NR(VIDIOC_G_AUDIO)] = "VIDIOC_G_AUDIO", - [_IOC_NR(VIDIOC_S_AUDIO)] = "VIDIOC_S_AUDIO", - [_IOC_NR(VIDIOC_QUERYCTRL)] = "VIDIOC_QUERYCTRL", - [_IOC_NR(VIDIOC_QUERYMENU)] = "VIDIOC_QUERYMENU", - [_IOC_NR(VIDIOC_G_INPUT)] = "VIDIOC_G_INPUT", - [_IOC_NR(VIDIOC_S_INPUT)] = "VIDIOC_S_INPUT", - [_IOC_NR(VIDIOC_G_OUTPUT)] = "VIDIOC_G_OUTPUT", - [_IOC_NR(VIDIOC_S_OUTPUT)] = "VIDIOC_S_OUTPUT", - [_IOC_NR(VIDIOC_ENUMOUTPUT)] = "VIDIOC_ENUMOUTPUT", - [_IOC_NR(VIDIOC_G_AUDOUT)] = "VIDIOC_G_AUDOUT", - [_IOC_NR(VIDIOC_S_AUDOUT)] = "VIDIOC_S_AUDOUT", - [_IOC_NR(VIDIOC_G_MODULATOR)] = "VIDIOC_G_MODULATOR", - [_IOC_NR(VIDIOC_S_MODULATOR)] = "VIDIOC_S_MODULATOR", - [_IOC_NR(VIDIOC_G_FREQUENCY)] = "VIDIOC_G_FREQUENCY", - [_IOC_NR(VIDIOC_S_FREQUENCY)] = "VIDIOC_S_FREQUENCY", - [_IOC_NR(VIDIOC_CROPCAP)] = "VIDIOC_CROPCAP", - [_IOC_NR(VIDIOC_G_CROP)] = "VIDIOC_G_CROP", - [_IOC_NR(VIDIOC_S_CROP)] = "VIDIOC_S_CROP", - [_IOC_NR(VIDIOC_G_SELECTION)] = "VIDIOC_G_SELECTION", - [_IOC_NR(VIDIOC_S_SELECTION)] = "VIDIOC_S_SELECTION", - [_IOC_NR(VIDIOC_G_JPEGCOMP)] = "VIDIOC_G_JPEGCOMP", - [_IOC_NR(VIDIOC_S_JPEGCOMP)] = "VIDIOC_S_JPEGCOMP", - [_IOC_NR(VIDIOC_QUERYSTD)] = "VIDIOC_QUERYSTD", - [_IOC_NR(VIDIOC_TRY_FMT)] = "VIDIOC_TRY_FMT", - [_IOC_NR(VIDIOC_ENUMAUDIO)] = "VIDIOC_ENUMAUDIO", - [_IOC_NR(VIDIOC_ENUMAUDOUT)] = "VIDIOC_ENUMAUDOUT", - [_IOC_NR(VIDIOC_G_PRIORITY)] = "VIDIOC_G_PRIORITY", - [_IOC_NR(VIDIOC_S_PRIORITY)] = "VIDIOC_S_PRIORITY", - [_IOC_NR(VIDIOC_G_SLICED_VBI_CAP)] = "VIDIOC_G_SLICED_VBI_CAP", - [_IOC_NR(VIDIOC_LOG_STATUS)] = "VIDIOC_LOG_STATUS", - [_IOC_NR(VIDIOC_G_EXT_CTRLS)] = "VIDIOC_G_EXT_CTRLS", - [_IOC_NR(VIDIOC_S_EXT_CTRLS)] = "VIDIOC_S_EXT_CTRLS", - [_IOC_NR(VIDIOC_TRY_EXT_CTRLS)] = "VIDIOC_TRY_EXT_CTRLS", -#if 1 - [_IOC_NR(VIDIOC_ENUM_FRAMESIZES)] = "VIDIOC_ENUM_FRAMESIZES", - [_IOC_NR(VIDIOC_ENUM_FRAMEINTERVALS)] = "VIDIOC_ENUM_FRAMEINTERVALS", - [_IOC_NR(VIDIOC_G_ENC_INDEX)] = "VIDIOC_G_ENC_INDEX", - [_IOC_NR(VIDIOC_ENCODER_CMD)] = "VIDIOC_ENCODER_CMD", - [_IOC_NR(VIDIOC_TRY_ENCODER_CMD)] = "VIDIOC_TRY_ENCODER_CMD", - [_IOC_NR(VIDIOC_DECODER_CMD)] = "VIDIOC_DECODER_CMD", - [_IOC_NR(VIDIOC_TRY_DECODER_CMD)] = "VIDIOC_TRY_DECODER_CMD", - [_IOC_NR(VIDIOC_DBG_S_REGISTER)] = "VIDIOC_DBG_S_REGISTER", - [_IOC_NR(VIDIOC_DBG_G_REGISTER)] = "VIDIOC_DBG_G_REGISTER", +struct v4l2_ioctl_info { + unsigned int ioctl; + u16 flags; + const char * const name; +}; - [_IOC_NR(VIDIOC_DBG_G_CHIP_IDENT)] = "VIDIOC_DBG_G_CHIP_IDENT", - [_IOC_NR(VIDIOC_S_HW_FREQ_SEEK)] = "VIDIOC_S_HW_FREQ_SEEK", +/* This control needs a priority check */ +#define INFO_FL_PRIO (1 << 0) +/* This control can be valid if the filehandle passes a control handler. */ +#define INFO_FL_CTRL (1 << 1) + +#define IOCTL_INFO(_ioctl, _flags) [_IOC_NR(_ioctl)] = { \ + .ioctl = _ioctl, \ + .flags = _flags, \ + .name = #_ioctl, \ +} + +static struct v4l2_ioctl_info v4l2_ioctls[] = { + IOCTL_INFO(VIDIOC_QUERYCAP, 0), + IOCTL_INFO(VIDIOC_ENUM_FMT, 0), + IOCTL_INFO(VIDIOC_G_FMT, 0), + IOCTL_INFO(VIDIOC_S_FMT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_REQBUFS, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QUERYBUF, 0), + IOCTL_INFO(VIDIOC_G_FBUF, 0), + IOCTL_INFO(VIDIOC_S_FBUF, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_OVERLAY, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QBUF, 0), + IOCTL_INFO(VIDIOC_DQBUF, 0), + IOCTL_INFO(VIDIOC_STREAMON, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_STREAMOFF, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_PARM, 0), + IOCTL_INFO(VIDIOC_S_PARM, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_STD, 0), + IOCTL_INFO(VIDIOC_S_STD, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_ENUMSTD, 0), + IOCTL_INFO(VIDIOC_ENUMINPUT, 0), + IOCTL_INFO(VIDIOC_G_CTRL, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_CTRL, INFO_FL_PRIO | INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_G_TUNER, 0), + IOCTL_INFO(VIDIOC_S_TUNER, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_AUDIO, 0), + IOCTL_INFO(VIDIOC_S_AUDIO, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QUERYCTRL, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_QUERYMENU, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_G_INPUT, 0), + IOCTL_INFO(VIDIOC_S_INPUT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_OUTPUT, 0), + IOCTL_INFO(VIDIOC_S_OUTPUT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_ENUMOUTPUT, 0), + IOCTL_INFO(VIDIOC_G_AUDOUT, 0), + IOCTL_INFO(VIDIOC_S_AUDOUT, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_MODULATOR, 0), + IOCTL_INFO(VIDIOC_S_MODULATOR, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_FREQUENCY, 0), + IOCTL_INFO(VIDIOC_S_FREQUENCY, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_CROPCAP, 0), + IOCTL_INFO(VIDIOC_G_CROP, 0), + IOCTL_INFO(VIDIOC_S_CROP, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_SELECTION, 0), + IOCTL_INFO(VIDIOC_S_SELECTION, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_JPEGCOMP, 0), + IOCTL_INFO(VIDIOC_S_JPEGCOMP, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_QUERYSTD, 0), + IOCTL_INFO(VIDIOC_TRY_FMT, 0), + IOCTL_INFO(VIDIOC_ENUMAUDIO, 0), + IOCTL_INFO(VIDIOC_ENUMAUDOUT, 0), + IOCTL_INFO(VIDIOC_G_PRIORITY, 0), + IOCTL_INFO(VIDIOC_S_PRIORITY, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_SLICED_VBI_CAP, 0), + IOCTL_INFO(VIDIOC_LOG_STATUS, 0), + IOCTL_INFO(VIDIOC_G_EXT_CTRLS, INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_S_EXT_CTRLS, INFO_FL_PRIO | INFO_FL_CTRL), + IOCTL_INFO(VIDIOC_TRY_EXT_CTRLS, 0), + IOCTL_INFO(VIDIOC_ENUM_FRAMESIZES, 0), + IOCTL_INFO(VIDIOC_ENUM_FRAMEINTERVALS, 0), + IOCTL_INFO(VIDIOC_G_ENC_INDEX, 0), + IOCTL_INFO(VIDIOC_ENCODER_CMD, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_TRY_ENCODER_CMD, 0), + IOCTL_INFO(VIDIOC_DECODER_CMD, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_TRY_DECODER_CMD, 0), +#ifdef CONFIG_VIDEO_ADV_DEBUG + IOCTL_INFO(VIDIOC_DBG_S_REGISTER, 0), + IOCTL_INFO(VIDIOC_DBG_G_REGISTER, 0), #endif - [_IOC_NR(VIDIOC_ENUM_DV_PRESETS)] = "VIDIOC_ENUM_DV_PRESETS", - [_IOC_NR(VIDIOC_S_DV_PRESET)] = "VIDIOC_S_DV_PRESET", - [_IOC_NR(VIDIOC_G_DV_PRESET)] = "VIDIOC_G_DV_PRESET", - [_IOC_NR(VIDIOC_QUERY_DV_PRESET)] = "VIDIOC_QUERY_DV_PRESET", - [_IOC_NR(VIDIOC_S_DV_TIMINGS)] = "VIDIOC_S_DV_TIMINGS", - [_IOC_NR(VIDIOC_G_DV_TIMINGS)] = "VIDIOC_G_DV_TIMINGS", - [_IOC_NR(VIDIOC_DQEVENT)] = "VIDIOC_DQEVENT", - [_IOC_NR(VIDIOC_SUBSCRIBE_EVENT)] = "VIDIOC_SUBSCRIBE_EVENT", - [_IOC_NR(VIDIOC_UNSUBSCRIBE_EVENT)] = "VIDIOC_UNSUBSCRIBE_EVENT", - [_IOC_NR(VIDIOC_CREATE_BUFS)] = "VIDIOC_CREATE_BUFS", - [_IOC_NR(VIDIOC_PREPARE_BUF)] = "VIDIOC_PREPARE_BUF", + IOCTL_INFO(VIDIOC_DBG_G_CHIP_IDENT, 0), + IOCTL_INFO(VIDIOC_S_HW_FREQ_SEEK, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_ENUM_DV_PRESETS, 0), + IOCTL_INFO(VIDIOC_S_DV_PRESET, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_DV_PRESET, 0), + IOCTL_INFO(VIDIOC_QUERY_DV_PRESET, 0), + IOCTL_INFO(VIDIOC_S_DV_TIMINGS, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_G_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_DQEVENT, 0), + IOCTL_INFO(VIDIOC_SUBSCRIBE_EVENT, 0), + IOCTL_INFO(VIDIOC_UNSUBSCRIBE_EVENT, 0), + IOCTL_INFO(VIDIOC_CREATE_BUFS, INFO_FL_PRIO), + IOCTL_INFO(VIDIOC_PREPARE_BUF, 0), + IOCTL_INFO(VIDIOC_ENUM_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_QUERY_DV_TIMINGS, 0), + IOCTL_INFO(VIDIOC_DV_TIMINGS_CAP, 0), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) +bool v4l2_is_known_ioctl(unsigned int cmd) +{ + if (_IOC_NR(cmd) >= V4L2_IOCTLS) + return false; + return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd; +} + /* Common ioctl debug function. This function can be used by external ioctl messages as well as internal V4L ioctl */ void v4l_printk_ioctl(unsigned int cmd) @@ -297,7 +309,7 @@ void v4l_printk_ioctl(unsigned int cmd) type = "v4l2"; break; } - printk("%s", v4l2_ioctls[_IOC_NR(cmd)]); + printk("%s", v4l2_ioctls[_IOC_NR(cmd)].name); return; default: type = "unknown"; @@ -359,6 +371,34 @@ static inline void dbgrect(struct video_device *vfd, char *s, r->width, r->height); }; +static void dbgtimings(struct video_device *vfd, + const struct v4l2_dv_timings *p) +{ + switch (p->type) { + case V4L2_DV_BT_656_1120: + dbgarg2("bt-656/1120:interlaced=%d," + " pixelclock=%lld," + " width=%d, height=%d, polarities=%x," + " hfrontporch=%d, hsync=%d," + " hbackporch=%d, vfrontporch=%d," + " vsync=%d, vbackporch=%d," + " il_vfrontporch=%d, il_vsync=%d," + " il_vbackporch=%d, standards=%x, flags=%x\n", + p->bt.interlaced, p->bt.pixelclock, + p->bt.width, p->bt.height, + p->bt.polarities, p->bt.hfrontporch, + p->bt.hsync, p->bt.hbackporch, + p->bt.vfrontporch, p->bt.vsync, + p->bt.vbackporch, p->bt.il_vfrontporch, + p->bt.il_vsync, p->bt.il_vbackporch, + p->bt.standards, p->bt.flags); + break; + default: + dbgarg2("Unknown type %d!\n", p->type); + break; + } +} + static inline void v4l_print_pix_fmt(struct video_device *vfd, struct v4l2_pix_format *fmt) { @@ -504,7 +544,6 @@ static long __video_do_ioctl(struct file *file, void *fh = file->private_data; struct v4l2_fh *vfh = NULL; int use_fh_prio = 0; - long ret_prio = 0; long ret = -ENOTTY; if (ops == NULL) { @@ -513,19 +552,30 @@ static long __video_do_ioctl(struct file *file, return ret; } - if ((vfd->debug & V4L2_DEBUG_IOCTL) && - !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { - v4l_print_ioctl(vfd->name, cmd); - printk(KERN_CONT "\n"); - } - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { vfh = file->private_data; use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); } - if (use_fh_prio) - ret_prio = v4l2_prio_check(vfd->prio, vfh->prio); + if (v4l2_is_known_ioctl(cmd)) { + struct v4l2_ioctl_info *info = &v4l2_ioctls[_IOC_NR(cmd)]; + + if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) && + !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) + return -ENOTTY; + + if (use_fh_prio && (info->flags & INFO_FL_PRIO)) { + ret = v4l2_prio_check(vfd->prio, vfh->prio); + if (ret) + return ret; + } + } + + if ((vfd->debug & V4L2_DEBUG_IOCTL) && + !(vfd->debug & V4L2_DEBUG_IOCTL_ARG)) { + v4l_print_ioctl(vfd->name, cmd); + printk(KERN_CONT "\n"); + } switch (cmd) { @@ -534,9 +584,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_capability *cap = (struct v4l2_capability *)arg; - if (!ops->vidioc_querycap) - break; - cap->version = LINUX_VERSION_CODE; ret = ops->vidioc_querycap(file, fh, cap); if (!ret) @@ -570,14 +617,11 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_priority *p = arg; - if (!ops->vidioc_s_priority && !use_fh_prio) - break; dbgarg(cmd, "setting priority to %d\n", *p); if (ops->vidioc_s_priority) ret = ops->vidioc_s_priority(file, fh, *p); else - ret = ret_prio ? ret_prio : - v4l2_prio_change(&vfd->v4l2_dev->prio, + ret = v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p); break; } @@ -587,6 +631,7 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_fmtdesc *f = arg; + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (likely(ops->vidioc_enum_fmt_vid_cap)) @@ -619,7 +664,7 @@ static long __video_do_ioctl(struct file *file, default: break; } - if (likely (!ret)) + if (likely(!ret)) dbgarg(cmd, "index=%d, type=%d, flags=%d, " "pixelformat=%c%c%c%c, description='%s'\n", f->index, f->type, f->flags, @@ -628,14 +673,6 @@ static long __video_do_ioctl(struct file *file, (f->pixelformat >> 16) & 0xff, (f->pixelformat >> 24) & 0xff, f->description); - else if (ret == -ENOTTY && - (ops->vidioc_enum_fmt_vid_cap || - ops->vidioc_enum_fmt_vid_out || - ops->vidioc_enum_fmt_vid_cap_mplane || - ops->vidioc_enum_fmt_vid_out_mplane || - ops->vidioc_enum_fmt_vid_overlay || - ops->vidioc_enum_fmt_type_private)) - ret = -EINVAL; break; } case VIDIOC_G_FMT: @@ -645,6 +682,7 @@ static long __video_do_ioctl(struct file *file, /* FIXME: Should be one dump per type */ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (ops->vidioc_g_fmt_vid_cap) @@ -706,21 +744,12 @@ static long __video_do_ioctl(struct file *file, fh, f); break; } - if (unlikely(ret == -ENOTTY && have_fmt_ops(g))) - ret = -EINVAL; - break; } case VIDIOC_S_FMT: { struct v4l2_format *f = (struct v4l2_format *)arg; - if (!have_fmt_ops(s)) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = -EINVAL; /* FIXME: Should be one dump per type */ @@ -804,6 +833,7 @@ static long __video_do_ioctl(struct file *file, /* FIXME: Should be one dump per type */ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names)); + ret = -EINVAL; switch (f->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: CLEAR_AFTER_FIELD(f, fmt.pix); @@ -876,8 +906,6 @@ static long __video_do_ioctl(struct file *file, fh, f); break; } - if (unlikely(ret == -ENOTTY && have_fmt_ops(try))) - ret = -EINVAL; break; } /* FIXME: Those buf reqs could be handled here, @@ -888,12 +916,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_requestbuffers *p = arg; - if (!ops->vidioc_reqbufs) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, p->type); if (ret) break; @@ -912,8 +934,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_querybuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -927,8 +947,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_qbuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -942,8 +960,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *p = arg; - if (!ops->vidioc_dqbuf) - break; ret = check_fmt(ops, p->type); if (ret) break; @@ -957,12 +973,6 @@ static long __video_do_ioctl(struct file *file, { int *i = arg; - if (!ops->vidioc_overlay) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_overlay(file, fh, *i); break; @@ -971,8 +981,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_framebuffer *p = arg; - if (!ops->vidioc_g_fbuf) - break; ret = ops->vidioc_g_fbuf(file, fh, arg); if (!ret) { dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", @@ -986,12 +994,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_framebuffer *p = arg; - if (!ops->vidioc_s_fbuf) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "capability=0x%x, flags=%d, base=0x%08lx\n", p->capability, p->flags, (unsigned long)p->base); v4l_print_pix_fmt(vfd, &p->fmt); @@ -1002,12 +1004,6 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_buf_type i = *(int *)arg; - if (!ops->vidioc_streamon) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); ret = ops->vidioc_streamon(file, fh, i); break; @@ -1016,12 +1012,6 @@ static long __video_do_ioctl(struct file *file, { enum v4l2_buf_type i = *(int *)arg; - if (!ops->vidioc_streamoff) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(i, v4l2_type_names)); ret = ops->vidioc_streamoff(file, fh, i); break; @@ -1091,13 +1081,6 @@ static long __video_do_ioctl(struct file *file, dbgarg(cmd, "std=%08Lx\n", (long long unsigned)*id); - if (!ops->vidioc_s_std) - break; - - if (ret_prio) { - ret = ret_prio; - break; - } ret = -EINVAL; norm = (*id) & vfd->tvnorms; if (vfd->tvnorms && !norm) /* Check if std is supported */ @@ -1115,8 +1098,6 @@ static long __video_do_ioctl(struct file *file, { v4l2_std_id *p = arg; - if (!ops->vidioc_querystd) - break; /* * If nothing detected, it should return all supported * Drivers just need to mask the std argument, in order @@ -1150,9 +1131,6 @@ static long __video_do_ioctl(struct file *file, if (ops->vidioc_s_dv_timings) p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS; - if (!ops->vidioc_enum_input) - break; - ret = ops->vidioc_enum_input(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, type=%d, " @@ -1168,8 +1146,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_g_input) - break; ret = ops->vidioc_g_input(file, fh, i); if (!ret) dbgarg(cmd, "value=%d\n", *i); @@ -1179,12 +1155,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_s_input) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_s_input(file, fh, *i); break; @@ -1195,9 +1165,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_output *p = arg; - if (!ops->vidioc_enum_output) - break; - /* * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS & * CAP_STD here based on ioctl handler provided by the @@ -1224,8 +1191,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_g_output) - break; ret = ops->vidioc_g_output(file, fh, i); if (!ret) dbgarg(cmd, "value=%d\n", *i); @@ -1235,12 +1200,6 @@ static long __video_do_ioctl(struct file *file, { unsigned int *i = arg; - if (!ops->vidioc_s_output) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "value=%d\n", *i); ret = ops->vidioc_s_output(file, fh, *i); break; @@ -1310,10 +1269,6 @@ static long __video_do_ioctl(struct file *file, if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && !ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls) break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value); @@ -1369,10 +1324,6 @@ static long __video_do_ioctl(struct file *file, if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls) break; - if (ret_prio) { - ret = ret_prio; - break; - } v4l_print_ext_ctrls(cmd, vfd, p, 1); if (vfh && vfh->ctrl_handler) ret = v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p); @@ -1428,8 +1379,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_enumaudio) - break; ret = ops->vidioc_enumaudio(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " @@ -1443,9 +1392,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_g_audio) - break; - ret = ops->vidioc_g_audio(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " @@ -1459,12 +1405,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audio *p = arg; - if (!ops->vidioc_s_audio) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=0x%x, " "mode=0x%x\n", p->index, p->name, p->capability, p->mode); @@ -1475,8 +1415,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_enumaudout) - break; dbgarg(cmd, "Enum for index=%d\n", p->index); ret = ops->vidioc_enumaudout(file, fh, p); if (!ret) @@ -1489,9 +1427,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_g_audout) - break; - ret = ops->vidioc_g_audout(file, fh, p); if (!ret) dbgarg2("index=%d, name=%s, capability=%d, " @@ -1503,12 +1438,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_audioout *p = arg; - if (!ops->vidioc_s_audout) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=%d, " "mode=%d\n", p->index, p->name, p->capability, p->mode); @@ -1520,8 +1449,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_modulator *p = arg; - if (!ops->vidioc_g_modulator) - break; ret = ops->vidioc_g_modulator(file, fh, p); if (!ret) dbgarg(cmd, "index=%d, name=%s, " @@ -1536,12 +1463,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_modulator *p = arg; - if (!ops->vidioc_s_modulator) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "index=%d, name=%s, capability=%d, " "rangelow=%d, rangehigh=%d, txsubchans=%d\n", p->index, p->name, p->capability, p->rangelow, @@ -1553,9 +1474,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_crop *p = arg; - if (!ops->vidioc_g_crop && !ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); if (ops->vidioc_g_crop) { @@ -1587,13 +1505,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_crop *p = arg; - if (!ops->vidioc_s_crop && !ops->vidioc_s_selection) - break; - - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); dbgrect(vfd, "", &p->c); @@ -1620,9 +1531,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_selection *p = arg; - if (!ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); ret = ops->vidioc_g_selection(file, fh, p); @@ -1634,13 +1542,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_selection *p = arg; - if (!ops->vidioc_s_selection) - break; - - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); dbgrect(vfd, "", &p->r); @@ -1653,9 +1554,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_cropcap *p = arg; /*FIXME: Should also show v4l2_fract pixelaspect */ - if (!ops->vidioc_cropcap && !ops->vidioc_g_selection) - break; - dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names)); if (ops->vidioc_cropcap) { ret = ops->vidioc_cropcap(file, fh, p); @@ -1699,9 +1597,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_jpegcompression *p = arg; - if (!ops->vidioc_g_jpegcomp) - break; - ret = ops->vidioc_g_jpegcomp(file, fh, p); if (!ret) dbgarg(cmd, "quality=%d, APPn=%d, " @@ -1715,12 +1610,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_jpegcompression *p = arg; - if (!ops->vidioc_g_jpegcomp) - break; - if (ret_prio) { - ret = ret_prio; - break; - } dbgarg(cmd, "quality=%d, APPn=%d, APP_len=%d, " "COM_len=%d, jpeg_markers=%d\n", p->quality, p->APPn, p->APP_len, @@ -1732,8 +1621,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_enc_idx *p = arg; - if (!ops->vidioc_g_enc_index) - break; ret = ops->vidioc_g_enc_index(file, fh, p); if (!ret) dbgarg(cmd, "entries=%d, entries_cap=%d\n", @@ -1744,12 +1631,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_encoder_cmd *p = arg; - if (!ops->vidioc_encoder_cmd) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = ops->vidioc_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1759,8 +1640,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_encoder_cmd *p = arg; - if (!ops->vidioc_try_encoder_cmd) - break; ret = ops->vidioc_try_encoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1770,12 +1649,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_decoder_cmd *p = arg; - if (!ops->vidioc_decoder_cmd) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = ops->vidioc_decoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1785,8 +1658,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_decoder_cmd *p = arg; - if (!ops->vidioc_try_decoder_cmd) - break; ret = ops->vidioc_try_decoder_cmd(file, fh, p); if (!ret) dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags); @@ -1796,8 +1667,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_streamparm *p = arg; - if (!ops->vidioc_g_parm && !vfd->current_norm) - break; if (ops->vidioc_g_parm) { ret = check_fmt(ops, p->type); if (ret) @@ -1825,12 +1694,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_streamparm *p = arg; - if (!ops->vidioc_s_parm) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, p->type); if (ret) break; @@ -1843,9 +1706,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_tuner *p = arg; - if (!ops->vidioc_g_tuner) - break; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ret = ops->vidioc_g_tuner(file, fh, p); @@ -1864,12 +1724,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_tuner *p = arg; - if (!ops->vidioc_s_tuner) - break; - if (ret_prio) { - ret = ret_prio; - break; - } p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, "index=%d, name=%s, type=%d, " @@ -1887,9 +1741,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frequency *p = arg; - if (!ops->vidioc_g_frequency) - break; - p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; ret = ops->vidioc_g_frequency(file, fh, p); @@ -1903,12 +1754,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_frequency *p = arg; enum v4l2_tuner_type type; - if (!ops->vidioc_s_frequency) - break; - if (ret_prio) { - ret = ret_prio; - break; - } type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n", @@ -1923,9 +1768,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_sliced_vbi_cap *p = arg; - if (!ops->vidioc_g_sliced_vbi_cap) - break; - /* Clear up to type, everything after type is zerod already */ memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type)); @@ -1937,8 +1779,6 @@ static long __video_do_ioctl(struct file *file, } case VIDIOC_LOG_STATUS: { - if (!ops->vidioc_log_status) - break; if (vfd->v4l2_dev) pr_info("%s: ================= START STATUS =================\n", vfd->v4l2_dev->name); @@ -1948,38 +1788,34 @@ static long __video_do_ioctl(struct file *file, vfd->v4l2_dev->name); break; } -#ifdef CONFIG_VIDEO_ADV_DEBUG case VIDIOC_DBG_G_REGISTER: { +#ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; - if (ops->vidioc_g_register) { - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ret = ops->vidioc_g_register(file, fh, p); - } + if (!capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ret = ops->vidioc_g_register(file, fh, p); +#endif break; } case VIDIOC_DBG_S_REGISTER: { +#ifdef CONFIG_VIDEO_ADV_DEBUG struct v4l2_dbg_register *p = arg; - if (ops->vidioc_s_register) { - if (!capable(CAP_SYS_ADMIN)) - ret = -EPERM; - else - ret = ops->vidioc_s_register(file, fh, p); - } + if (!capable(CAP_SYS_ADMIN)) + ret = -EPERM; + else + ret = ops->vidioc_s_register(file, fh, p); +#endif break; } -#endif case VIDIOC_DBG_G_CHIP_IDENT: { struct v4l2_dbg_chip_ident *p = arg; - if (!ops->vidioc_g_chip_ident) - break; p->ident = V4L2_IDENT_NONE; p->revision = 0; ret = ops->vidioc_g_chip_ident(file, fh, p); @@ -1992,12 +1828,6 @@ static long __video_do_ioctl(struct file *file, struct v4l2_hw_freq_seek *p = arg; enum v4l2_tuner_type type; - if (!ops->vidioc_s_hw_freq_seek) - break; - if (ret_prio) { - ret = ret_prio; - break; - } type = (vfd->vfl_type == VFL_TYPE_RADIO) ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV; dbgarg(cmd, @@ -2013,9 +1843,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frmsizeenum *p = arg; - if (!ops->vidioc_enum_framesizes) - break; - ret = ops->vidioc_enum_framesizes(file, fh, p); dbgarg(cmd, "index=%d, pixelformat=%c%c%c%c, type=%d ", @@ -2049,9 +1876,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_frmivalenum *p = arg; - if (!ops->vidioc_enum_frameintervals) - break; - ret = ops->vidioc_enum_frameintervals(file, fh, p); dbgarg(cmd, "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ", @@ -2084,9 +1908,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_enum_preset *p = arg; - if (!ops->vidioc_enum_dv_presets) - break; - ret = ops->vidioc_enum_dv_presets(file, fh, p); if (!ret) dbgarg(cmd, @@ -2100,13 +1921,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_s_dv_preset) - break; - if (ret_prio) { - ret = ret_prio; - break; - } - dbgarg(cmd, "preset=%d\n", p->preset); ret = ops->vidioc_s_dv_preset(file, fh, p); break; @@ -2115,9 +1929,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_g_dv_preset) - break; - ret = ops->vidioc_g_dv_preset(file, fh, p); if (!ret) dbgarg(cmd, "preset=%d\n", p->preset); @@ -2127,9 +1938,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_preset *p = arg; - if (!ops->vidioc_query_dv_preset) - break; - ret = ops->vidioc_query_dv_preset(file, fh, p); if (!ret) dbgarg(cmd, "preset=%d\n", p->preset); @@ -2139,32 +1947,13 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_timings *p = arg; - if (!ops->vidioc_s_dv_timings) - break; - if (ret_prio) { - ret = ret_prio; - break; - } - + dbgtimings(vfd, p); switch (p->type) { case V4L2_DV_BT_656_1120: - dbgarg2("bt-656/1120:interlaced=%d, pixelclock=%lld," - " width=%d, height=%d, polarities=%x," - " hfrontporch=%d, hsync=%d, hbackporch=%d," - " vfrontporch=%d, vsync=%d, vbackporch=%d," - " il_vfrontporch=%d, il_vsync=%d," - " il_vbackporch=%d\n", - p->bt.interlaced, p->bt.pixelclock, - p->bt.width, p->bt.height, p->bt.polarities, - p->bt.hfrontporch, p->bt.hsync, - p->bt.hbackporch, p->bt.vfrontporch, - p->bt.vsync, p->bt.vbackporch, - p->bt.il_vfrontporch, p->bt.il_vsync, - p->bt.il_vbackporch); ret = ops->vidioc_s_dv_timings(file, fh, p); break; default: - dbgarg2("Unknown type %d!\n", p->type); + ret = -EINVAL; break; } break; @@ -2173,33 +1962,61 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_dv_timings *p = arg; - if (!ops->vidioc_g_dv_timings) + ret = ops->vidioc_g_dv_timings(file, fh, p); + if (!ret) + dbgtimings(vfd, p); + break; + } + case VIDIOC_ENUM_DV_TIMINGS: + { + struct v4l2_enum_dv_timings *p = arg; + + if (!ops->vidioc_enum_dv_timings) break; - ret = ops->vidioc_g_dv_timings(file, fh, p); + ret = ops->vidioc_enum_dv_timings(file, fh, p); if (!ret) { - switch (p->type) { - case V4L2_DV_BT_656_1120: - dbgarg2("bt-656/1120:interlaced=%d," - " pixelclock=%lld," - " width=%d, height=%d, polarities=%x," - " hfrontporch=%d, hsync=%d," - " hbackporch=%d, vfrontporch=%d," - " vsync=%d, vbackporch=%d," - " il_vfrontporch=%d, il_vsync=%d," - " il_vbackporch=%d\n", - p->bt.interlaced, p->bt.pixelclock, - p->bt.width, p->bt.height, - p->bt.polarities, p->bt.hfrontporch, - p->bt.hsync, p->bt.hbackporch, - p->bt.vfrontporch, p->bt.vsync, - p->bt.vbackporch, p->bt.il_vfrontporch, - p->bt.il_vsync, p->bt.il_vbackporch); - break; - default: - dbgarg2("Unknown type %d!\n", p->type); - break; - } + dbgarg(cmd, "index=%d: ", p->index); + dbgtimings(vfd, &p->timings); + } + break; + } + case VIDIOC_QUERY_DV_TIMINGS: + { + struct v4l2_dv_timings *p = arg; + + if (!ops->vidioc_query_dv_timings) + break; + + ret = ops->vidioc_query_dv_timings(file, fh, p); + if (!ret) + dbgtimings(vfd, p); + break; + } + case VIDIOC_DV_TIMINGS_CAP: + { + struct v4l2_dv_timings_cap *p = arg; + + if (!ops->vidioc_dv_timings_cap) + break; + + ret = ops->vidioc_dv_timings_cap(file, fh, p); + if (ret) + break; + switch (p->type) { + case V4L2_DV_BT_656_1120: + dbgarg(cmd, + "type=%d, width=%u-%u, height=%u-%u, " + "pixelclock=%llu-%llu, standards=%x, capabilities=%x ", + p->type, + p->bt.min_width, p->bt.max_width, + p->bt.min_height, p->bt.max_height, + p->bt.min_pixelclock, p->bt.max_pixelclock, + p->bt.standards, p->bt.capabilities); + break; + default: + dbgarg(cmd, "unknown type "); + break; } break; } @@ -2207,9 +2024,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event *ev = arg; - if (!ops->vidioc_subscribe_event) - break; - ret = v4l2_event_dequeue(fh, ev, file->f_flags & O_NONBLOCK); if (ret < 0) { dbgarg(cmd, "no pending events?"); @@ -2226,9 +2040,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event_subscription *sub = arg; - if (!ops->vidioc_subscribe_event) - break; - ret = ops->vidioc_subscribe_event(fh, sub); if (ret < 0) { dbgarg(cmd, "failed, ret=%ld", ret); @@ -2241,9 +2052,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_event_subscription *sub = arg; - if (!ops->vidioc_unsubscribe_event) - break; - ret = ops->vidioc_unsubscribe_event(fh, sub); if (ret < 0) { dbgarg(cmd, "failed, ret=%ld", ret); @@ -2256,12 +2064,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_create_buffers *create = arg; - if (!ops->vidioc_create_bufs) - break; - if (ret_prio) { - ret = ret_prio; - break; - } ret = check_fmt(ops, create->format.type); if (ret) break; @@ -2275,8 +2077,6 @@ static long __video_do_ioctl(struct file *file, { struct v4l2_buffer *b = arg; - if (!ops->vidioc_prepare_buf) - break; ret = check_fmt(ops, b->type); if (ret) break; @@ -2289,7 +2089,9 @@ static long __video_do_ioctl(struct file *file, default: if (!ops->vidioc_default) break; - ret = ops->vidioc_default(file, fh, ret_prio >= 0, cmd, arg); + ret = ops->vidioc_default(file, fh, use_fh_prio ? + v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, + cmd, arg); break; } /* switch */ @@ -2463,7 +2265,9 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, err = -EFAULT; goto out_array_args; } - if (err < 0) + /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid + results that must be returned. */ + if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS) goto out; out_array_args: diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c index 6fe88e965a8c..db6e859b93d4 100644 --- a/drivers/media/video/v4l2-subdev.c +++ b/drivers/media/video/v4l2-subdev.c @@ -35,14 +35,9 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - /* Allocate try format and crop in the same memory block */ - fh->try_fmt = kzalloc((sizeof(*fh->try_fmt) + sizeof(*fh->try_crop)) - * sd->entity.num_pads, GFP_KERNEL); - if (fh->try_fmt == NULL) + fh->pad = kzalloc(sizeof(*fh->pad) * sd->entity.num_pads, GFP_KERNEL); + if (fh->pad == NULL) return -ENOMEM; - - fh->try_crop = (struct v4l2_rect *) - (fh->try_fmt + sd->entity.num_pads); #endif return 0; } @@ -50,9 +45,8 @@ static int subdev_fh_init(struct v4l2_subdev_fh *fh, struct v4l2_subdev *sd) static void subdev_fh_free(struct v4l2_subdev_fh *fh) { #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - kfree(fh->try_fmt); - fh->try_fmt = NULL; - fh->try_crop = NULL; + kfree(fh->pad); + fh->pad = NULL; #endif } @@ -234,6 +228,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_CROP: { struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + int rval; if (crop->which != V4L2_SUBDEV_FORMAT_TRY && crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -242,11 +238,27 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (crop->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); + if (rval != -ENOIOCTLCMD) + return rval; + + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; + sel.pad = crop->pad; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + + rval = v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, &sel); + + crop->rect = sel.r; + + return rval; } case VIDIOC_SUBDEV_S_CROP: { struct v4l2_subdev_crop *crop = arg; + struct v4l2_subdev_selection sel; + int rval; if (crop->which != V4L2_SUBDEV_FORMAT_TRY && crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) @@ -255,7 +267,22 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (crop->pad >= sd->entity.num_pads) return -EINVAL; - return v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); + if (rval != -ENOIOCTLCMD) + return rval; + + memset(&sel, 0, sizeof(sel)); + sel.which = crop->which; + sel.pad = crop->pad; + sel.target = V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL; + sel.r = crop->rect; + + rval = v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, &sel); + + crop->rect = sel.r; + + return rval; } case VIDIOC_SUBDEV_ENUM_MBUS_CODE: { @@ -293,6 +320,34 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) return v4l2_subdev_call(sd, pad, enum_frame_interval, subdev_fh, fie); } + + case VIDIOC_SUBDEV_G_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, get_selection, subdev_fh, sel); + } + + case VIDIOC_SUBDEV_S_SELECTION: { + struct v4l2_subdev_selection *sel = arg; + + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return v4l2_subdev_call( + sd, pad, set_selection, subdev_fh, sel); + } #endif default: return v4l2_subdev_call(sd, core, ioctl, cmd, arg); @@ -332,6 +387,70 @@ const struct v4l2_file_operations v4l2_subdev_fops = { .poll = subdev_poll, }; +#ifdef CONFIG_MEDIA_CONTROLLER +int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt) +{ + if (source_fmt->format.width != sink_fmt->format.width + || source_fmt->format.height != sink_fmt->format.height + || source_fmt->format.code != sink_fmt->format.code) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate_default); + +static int +v4l2_subdev_link_validate_get_format(struct media_pad *pad, + struct v4l2_subdev_format *fmt) +{ + switch (media_entity_type(pad->entity)) { + case MEDIA_ENT_T_V4L2_SUBDEV: + fmt->which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt->pad = pad->index; + return v4l2_subdev_call(media_entity_to_v4l2_subdev( + pad->entity), + pad, get_fmt, NULL, fmt); + default: + WARN(1, "Driver bug! Wrong media entity type %d, entity %s\n", + media_entity_type(pad->entity), pad->entity->name); + /* Fall through */ + case MEDIA_ENT_T_DEVNODE_V4L: + return -EINVAL; + } +} + +int v4l2_subdev_link_validate(struct media_link *link) +{ + struct v4l2_subdev *sink; + struct v4l2_subdev_format sink_fmt, source_fmt; + int rval; + + rval = v4l2_subdev_link_validate_get_format( + link->source, &source_fmt); + if (rval < 0) + return 0; + + rval = v4l2_subdev_link_validate_get_format( + link->sink, &sink_fmt); + if (rval < 0) + return 0; + + sink = media_entity_to_v4l2_subdev(link->sink->entity); + + rval = v4l2_subdev_call(sink, pad, link_validate, link, + &source_fmt, &sink_fmt); + if (rval != -ENOIOCTLCMD) + return rval; + + return v4l2_subdev_link_validate_default( + sink, link, &source_fmt, &sink_fmt); +} +EXPORT_SYMBOL_GPL(v4l2_subdev_link_validate); +#endif /* CONFIG_MEDIA_CONTROLLER */ + void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops) { INIT_LIST_HEAD(&sd->list); diff --git a/drivers/media/video/via-camera.c b/drivers/media/video/via-camera.c index 20f7237b8242..308e150a39bc 100644 --- a/drivers/media/video/via-camera.c +++ b/drivers/media/video/via-camera.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -1347,11 +1348,21 @@ static __devinit bool viacam_serial_is_enabled(void) return false; } +static struct ov7670_config sensor_cfg = { + /* The XO-1.5 (only known user) clocks the camera at 90MHz. */ + .clock_speed = 90, +}; + static __devinit int viacam_probe(struct platform_device *pdev) { int ret; struct i2c_adapter *sensor_adapter; struct viafb_dev *viadev = pdev->dev.platform_data; + struct i2c_board_info ov7670_info = { + .type = "ov7670", + .addr = 0x42 >> 1, + .platform_data = &sensor_cfg, + }; /* * Note that there are actually two capture channels on @@ -1433,8 +1444,8 @@ static __devinit int viacam_probe(struct platform_device *pdev) * is OLPC-specific. 0x42 assumption is ov7670-specific. */ sensor_adapter = viafb_find_i2c_adapter(VIA_PORT_31); - cam->sensor = v4l2_i2c_new_subdev(&cam->v4l2_dev, sensor_adapter, - "ov7670", 0x42 >> 1, NULL); + cam->sensor = v4l2_i2c_new_subdev_board(&cam->v4l2_dev, sensor_adapter, + &ov7670_info, NULL); if (cam->sensor == NULL) { dev_err(&pdev->dev, "Unable to find the sensor!\n"); ret = -ENODEV; diff --git a/drivers/media/video/videobuf-core.c b/drivers/media/video/videobuf-core.c index de4fa4eb8844..ffdf59cfe405 100644 --- a/drivers/media/video/videobuf-core.c +++ b/drivers/media/video/videobuf-core.c @@ -1129,6 +1129,7 @@ unsigned int videobuf_poll_stream(struct file *file, struct videobuf_queue *q, poll_table *wait) { + unsigned long req_events = poll_requested_events(wait); struct videobuf_buffer *buf = NULL; unsigned int rc = 0; @@ -1137,7 +1138,7 @@ unsigned int videobuf_poll_stream(struct file *file, if (!list_empty(&q->stream)) buf = list_entry(q->stream.next, struct videobuf_buffer, stream); - } else { + } else if (req_events & (POLLIN | POLLRDNORM)) { if (!q->reading) __videobuf_read_start(q); if (!q->reading) { diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c index c9691115f2d2..b6b5cc1a43cb 100644 --- a/drivers/media/video/videobuf-dma-contig.c +++ b/drivers/media/video/videobuf-dma-contig.c @@ -27,6 +27,7 @@ struct videobuf_dma_contig_memory { u32 magic; void *vaddr; dma_addr_t dma_handle; + bool cached; unsigned long size; }; @@ -37,8 +38,58 @@ struct videobuf_dma_contig_memory { BUG(); \ } -static void -videobuf_vm_open(struct vm_area_struct *vma) +static int __videobuf_dc_alloc(struct device *dev, + struct videobuf_dma_contig_memory *mem, + unsigned long size, unsigned long flags) +{ + mem->size = size; + if (mem->cached) { + mem->vaddr = alloc_pages_exact(mem->size, flags | GFP_DMA); + if (mem->vaddr) { + int err; + + mem->dma_handle = dma_map_single(dev, mem->vaddr, + mem->size, + DMA_FROM_DEVICE); + err = dma_mapping_error(dev, mem->dma_handle); + if (err) { + dev_err(dev, "dma_map_single failed\n"); + + free_pages_exact(mem->vaddr, mem->size); + mem->vaddr = 0; + return err; + } + } + } else + mem->vaddr = dma_alloc_coherent(dev, mem->size, + &mem->dma_handle, flags); + + if (!mem->vaddr) { + dev_err(dev, "memory alloc size %ld failed\n", mem->size); + return -ENOMEM; + } + + dev_dbg(dev, "dma mapped data is at %p (%ld)\n", mem->vaddr, mem->size); + + return 0; +} + +static void __videobuf_dc_free(struct device *dev, + struct videobuf_dma_contig_memory *mem) +{ + if (mem->cached) { + if (!mem->vaddr) + return; + dma_unmap_single(dev, mem->dma_handle, mem->size, + DMA_FROM_DEVICE); + free_pages_exact(mem->vaddr, mem->size); + } else + dma_free_coherent(dev, mem->size, mem->vaddr, mem->dma_handle); + + mem->vaddr = NULL; +} + +static void videobuf_vm_open(struct vm_area_struct *vma) { struct videobuf_mapping *map = vma->vm_private_data; @@ -91,12 +142,11 @@ static void videobuf_vm_close(struct vm_area_struct *vma) dev_dbg(q->dev, "buf[%d] freeing %p\n", i, mem->vaddr); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); + __videobuf_dc_free(q->dev, mem); mem->vaddr = NULL; } - q->bufs[i]->map = NULL; + q->bufs[i]->map = NULL; q->bufs[i]->baddr = 0; } @@ -107,8 +157,8 @@ static void videobuf_vm_close(struct vm_area_struct *vma) } static const struct vm_operations_struct videobuf_vm_ops = { - .open = videobuf_vm_open, - .close = videobuf_vm_close, + .open = videobuf_vm_open, + .close = videobuf_vm_close, }; /** @@ -178,26 +228,38 @@ static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, pages_done++; } - out_up: +out_up: up_read(¤t->mm->mmap_sem); return ret; } -static struct videobuf_buffer *__videobuf_alloc_vb(size_t size) +static struct videobuf_buffer *__videobuf_alloc_vb(size_t size, bool cached) { struct videobuf_dma_contig_memory *mem; struct videobuf_buffer *vb; vb = kzalloc(size + sizeof(*mem), GFP_KERNEL); if (vb) { - mem = vb->priv = ((char *)vb) + size; + vb->priv = ((char *)vb) + size; + mem = vb->priv; mem->magic = MAGIC_DC_MEM; + mem->cached = cached; } return vb; } +static struct videobuf_buffer *__videobuf_alloc_uncached(size_t size) +{ + return __videobuf_alloc_vb(size, false); +} + +static struct videobuf_buffer *__videobuf_alloc_cached(size_t size) +{ + return __videobuf_alloc_vb(size, true); +} + static void *__videobuf_to_vaddr(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -235,28 +297,32 @@ static int __videobuf_iolock(struct videobuf_queue *q, return videobuf_dma_contig_user_get(mem, vb); /* allocate memory for the read() method */ - mem->size = PAGE_ALIGN(vb->size); - mem->vaddr = dma_alloc_coherent(q->dev, mem->size, - &mem->dma_handle, GFP_KERNEL); - if (!mem->vaddr) { - dev_err(q->dev, "dma_alloc_coherent %ld failed\n", - mem->size); + if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(vb->size), + GFP_KERNEL)) return -ENOMEM; - } - - dev_dbg(q->dev, "dma_alloc_coherent data is at %p (%ld)\n", - mem->vaddr, mem->size); break; case V4L2_MEMORY_OVERLAY: default: - dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", - __func__); + dev_dbg(q->dev, "%s memory method OVERLAY/unknown\n", __func__); return -EINVAL; } return 0; } +static int __videobuf_sync(struct videobuf_queue *q, + struct videobuf_buffer *buf) +{ + struct videobuf_dma_contig_memory *mem = buf->priv; + BUG_ON(!mem); + MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + + dma_sync_single_for_cpu(q->dev, mem->dma_handle, mem->size, + DMA_FROM_DEVICE); + + return 0; +} + static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_buffer *buf, struct vm_area_struct *vma) @@ -265,6 +331,8 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, struct videobuf_mapping *map; int retval; unsigned long size; + unsigned long pos, start = vma->vm_start; + struct page *page; dev_dbg(q->dev, "%s\n", __func__); @@ -282,41 +350,50 @@ static int __videobuf_mmap_mapper(struct videobuf_queue *q, BUG_ON(!mem); MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); - mem->size = PAGE_ALIGN(buf->bsize); - mem->vaddr = dma_alloc_coherent(q->dev, mem->size, - &mem->dma_handle, GFP_KERNEL); - if (!mem->vaddr) { - dev_err(q->dev, "dma_alloc_coherent size %ld failed\n", - mem->size); + if (__videobuf_dc_alloc(q->dev, mem, PAGE_ALIGN(buf->bsize), + GFP_KERNEL | __GFP_COMP)) goto error; - } - dev_dbg(q->dev, "dma_alloc_coherent data is at addr %p (size %ld)\n", - mem->vaddr, mem->size); /* Try to remap memory */ size = vma->vm_end - vma->vm_start; size = (size < mem->size) ? size : mem->size; - vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); - retval = remap_pfn_range(vma, vma->vm_start, - mem->dma_handle >> PAGE_SHIFT, - size, vma->vm_page_prot); - if (retval) { - dev_err(q->dev, "mmap: remap failed with error %d. ", retval); - dma_free_coherent(q->dev, mem->size, - mem->vaddr, mem->dma_handle); - goto error; + if (!mem->cached) + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + pos = (unsigned long)mem->vaddr; + + while (size > 0) { + page = virt_to_page((void *)pos); + if (NULL == page) { + dev_err(q->dev, "mmap: virt_to_page failed\n"); + __videobuf_dc_free(q->dev, mem); + goto error; + } + retval = vm_insert_page(vma, start, page); + if (retval) { + dev_err(q->dev, "mmap: insert failed with error %d\n", + retval); + __videobuf_dc_free(q->dev, mem); + goto error; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } - vma->vm_ops = &videobuf_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; + vma->vm_ops = &videobuf_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; vma->vm_private_data = map; dev_dbg(q->dev, "mmap %p: q=%p %08lx-%08lx (%lx) pgoff %08lx buf %d\n", map, q, vma->vm_start, vma->vm_end, - (long int)buf->bsize, - vma->vm_pgoff, buf->i); + (long int)buf->bsize, vma->vm_pgoff, buf->i); videobuf_vm_open(vma); @@ -328,12 +405,20 @@ error: } static struct videobuf_qtype_ops qops = { - .magic = MAGIC_QTYPE_OPS, + .magic = MAGIC_QTYPE_OPS, + .alloc_vb = __videobuf_alloc_uncached, + .iolock = __videobuf_iolock, + .mmap_mapper = __videobuf_mmap_mapper, + .vaddr = __videobuf_to_vaddr, +}; - .alloc_vb = __videobuf_alloc_vb, - .iolock = __videobuf_iolock, - .mmap_mapper = __videobuf_mmap_mapper, - .vaddr = __videobuf_to_vaddr, +static struct videobuf_qtype_ops qops_cached = { + .magic = MAGIC_QTYPE_OPS, + .alloc_vb = __videobuf_alloc_cached, + .iolock = __videobuf_iolock, + .sync = __videobuf_sync, + .mmap_mapper = __videobuf_mmap_mapper, + .vaddr = __videobuf_to_vaddr, }; void videobuf_queue_dma_contig_init(struct videobuf_queue *q, @@ -351,6 +436,20 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, } EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init); +void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, + const struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv, struct mutex *ext_lock) +{ + videobuf_queue_core_init(q, ops, dev, irqlock, type, field, msize, + priv, &qops_cached, ext_lock); +} +EXPORT_SYMBOL_GPL(videobuf_queue_dma_contig_init_cached); + dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf) { struct videobuf_dma_contig_memory *mem = buf->priv; @@ -389,7 +488,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, /* read() method */ if (mem->vaddr) { - dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle); + __videobuf_dc_free(q->dev, mem); mem->vaddr = NULL; } } diff --git a/drivers/media/video/videobuf-dvb.c b/drivers/media/video/videobuf-dvb.c index 59cb54aa2946..94d83a41381b 100644 --- a/drivers/media/video/videobuf-dvb.c +++ b/drivers/media/video/videobuf-dvb.c @@ -45,7 +45,6 @@ static int videobuf_dvb_thread(void *data) struct videobuf_dvb *dvb = data; struct videobuf_buffer *buf; unsigned long flags; - int err; void *outp; dprintk("dvb thread started\n"); @@ -57,7 +56,7 @@ static int videobuf_dvb_thread(void *data) buf = list_entry(dvb->dvbq.stream.next, struct videobuf_buffer, stream); list_del(&buf->stream); - err = videobuf_waiton(&dvb->dvbq, buf, 0, 1); + videobuf_waiton(&dvb->dvbq, buf, 0, 1); /* no more feeds left or stop_feed() asked us to quit */ if (0 == dvb->nfeeds) diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c index 2e8f1df775b6..9d4e9edbd2e7 100644 --- a/drivers/media/video/videobuf2-core.c +++ b/drivers/media/video/videobuf2-core.c @@ -19,6 +19,9 @@ #include #include +#include +#include +#include #include static int debug; @@ -1642,32 +1645,46 @@ static int __vb2_cleanup_fileio(struct vb2_queue *q); * For OUTPUT queues, if a buffer is ready to be dequeued, the file descriptor * will be reported as available for writing. * + * If the driver uses struct v4l2_fh, then vb2_poll() will also check for any + * pending events. + * * The return values from this function are intended to be directly returned * from poll handler in driver. */ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) { - unsigned long flags; - unsigned int ret; + struct video_device *vfd = video_devdata(file); + unsigned long req_events = poll_requested_events(wait); struct vb2_buffer *vb = NULL; + unsigned int res = 0; + unsigned long flags; + + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { + struct v4l2_fh *fh = file->private_data; + + if (v4l2_event_pending(fh)) + res = POLLPRI; + else if (req_events & POLLPRI) + poll_wait(file, &fh->wait, wait); + } /* * Start file I/O emulator only if streaming API has not been used yet. */ if (q->num_buffers == 0 && q->fileio == NULL) { - if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) { - ret = __vb2_init_fileio(q, 1); - if (ret) - return POLLERR; + if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ) && + (req_events & (POLLIN | POLLRDNORM))) { + if (__vb2_init_fileio(q, 1)) + return res | POLLERR; } - if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) { - ret = __vb2_init_fileio(q, 0); - if (ret) - return POLLERR; + if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE) && + (req_events & (POLLOUT | POLLWRNORM))) { + if (__vb2_init_fileio(q, 0)) + return res | POLLERR; /* * Write to OUTPUT queue can be done immediately. */ - return POLLOUT | POLLWRNORM; + return res | POLLOUT | POLLWRNORM; } } @@ -1675,7 +1692,7 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) * There is nothing to wait for if no buffers have already been queued. */ if (list_empty(&q->queued_list)) - return POLLERR; + return res | POLLERR; poll_wait(file, &q->done_wq, wait); @@ -1690,10 +1707,11 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) if (vb && (vb->state == VB2_BUF_STATE_DONE || vb->state == VB2_BUF_STATE_ERROR)) { - return (V4L2_TYPE_IS_OUTPUT(q->type)) ? POLLOUT | POLLWRNORM : - POLLIN | POLLRDNORM; + return (V4L2_TYPE_IS_OUTPUT(q->type)) ? + res | POLLOUT | POLLWRNORM : + res | POLLIN | POLLRDNORM; } - return 0; + return res; } EXPORT_SYMBOL_GPL(vb2_poll); @@ -1839,7 +1857,6 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) * (multiplane buffers are not supported). */ if (q->bufs[0]->num_planes != 1) { - fileio->req.count = 0; ret = -EBUSY; goto err_reqbufs; } @@ -1886,6 +1903,7 @@ static int __vb2_init_fileio(struct vb2_queue *q, int read) return ret; err_reqbufs: + fileio->req.count = 0; vb2_reqbufs(q, &fileio->req); err_kfree: diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c index 5e8b0710105b..0960d7f0d394 100644 --- a/drivers/media/video/vivi.c +++ b/drivers/media/video/vivi.c @@ -94,6 +94,16 @@ static struct vivi_fmt formats[] = { .fourcc = V4L2_PIX_FMT_UYVY, .depth = 16, }, + { + .name = "4:2:2, packed, YVYU", + .fourcc = V4L2_PIX_FMT_YVYU, + .depth = 16, + }, + { + .name = "4:2:2, packed, VYUY", + .fourcc = V4L2_PIX_FMT_VYUY, + .depth = 16, + }, { .name = "RGB565 (LE)", .fourcc = V4L2_PIX_FMT_RGB565, /* gggbbbbb rrrrrggg */ @@ -114,6 +124,26 @@ static struct vivi_fmt formats[] = { .fourcc = V4L2_PIX_FMT_RGB555X, /* arrrrrgg gggbbbbb */ .depth = 16, }, + { + .name = "RGB24 (LE)", + .fourcc = V4L2_PIX_FMT_RGB24, /* rgb */ + .depth = 24, + }, + { + .name = "RGB24 (BE)", + .fourcc = V4L2_PIX_FMT_BGR24, /* bgr */ + .depth = 24, + }, + { + .name = "RGB32 (LE)", + .fourcc = V4L2_PIX_FMT_RGB32, /* argb */ + .depth = 32, + }, + { + .name = "RGB32 (BE)", + .fourcc = V4L2_PIX_FMT_BGR32, /* bgra */ + .depth = 32, + }, }; static struct vivi_fmt *get_format(struct v4l2_format *f) @@ -170,6 +200,7 @@ struct vivi_dev { struct v4l2_ctrl *gain; }; struct v4l2_ctrl *volume; + struct v4l2_ctrl *alpha; struct v4l2_ctrl *button; struct v4l2_ctrl *boolean; struct v4l2_ctrl *int32; @@ -177,6 +208,7 @@ struct vivi_dev { struct v4l2_ctrl *menu; struct v4l2_ctrl *string; struct v4l2_ctrl *bitmask; + struct v4l2_ctrl *int_menu; spinlock_t slock; struct mutex mutex; @@ -203,8 +235,10 @@ struct vivi_dev { enum v4l2_field field; unsigned int field_count; - u8 bars[9][3]; - u8 line[MAX_WIDTH * 4]; + u8 bars[9][3]; + u8 line[MAX_WIDTH * 8]; + unsigned int pixelsize; + u8 alpha_component; }; /* ------------------------------------------------------------------ @@ -283,6 +317,8 @@ static void precalculate_bars(struct vivi_dev *dev) switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YVYU: + case V4L2_PIX_FMT_VYUY: is_yuv = 1; break; case V4L2_PIX_FMT_RGB565: @@ -297,6 +333,11 @@ static void precalculate_bars(struct vivi_dev *dev) g >>= 3; b >>= 3; break; + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: + break; } if (is_yuv) { @@ -316,9 +357,11 @@ static void precalculate_bars(struct vivi_dev *dev) #define TSTAMP_INPUT_X 10 #define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X) -static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) +/* 'odd' is true for pixels 1, 3, 5, etc. and false for pixels 0, 2, 4, etc. */ +static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos, bool odd) { u8 r_y, g_u, b_v; + u8 alpha = dev->alpha_component; int color; u8 *p; @@ -326,46 +369,56 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) g_u = dev->bars[colorpos][1]; /* G or precalculated U */ b_v = dev->bars[colorpos][2]; /* B or precalculated V */ - for (color = 0; color < 4; color++) { + for (color = 0; color < dev->pixelsize; color++) { p = buf + color; switch (dev->fmt->fourcc) { case V4L2_PIX_FMT_YUYV: switch (color) { case 0: - case 2: *p = r_y; break; case 1: - *p = g_u; - break; - case 3: - *p = b_v; + *p = odd ? b_v : g_u; break; } break; case V4L2_PIX_FMT_UYVY: switch (color) { + case 0: + *p = odd ? b_v : g_u; + break; case 1: - case 3: *p = r_y; break; + } + break; + case V4L2_PIX_FMT_YVYU: + switch (color) { case 0: - *p = g_u; + *p = r_y; break; - case 2: - *p = b_v; + case 1: + *p = odd ? g_u : b_v; + break; + } + break; + case V4L2_PIX_FMT_VYUY: + switch (color) { + case 0: + *p = odd ? g_u : b_v; + break; + case 1: + *p = r_y; break; } break; case V4L2_PIX_FMT_RGB565: switch (color) { case 0: - case 2: *p = (g_u << 5) | b_v; break; case 1: - case 3: *p = (r_y << 3) | (g_u >> 3); break; } @@ -373,11 +426,9 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) case V4L2_PIX_FMT_RGB565X: switch (color) { case 0: - case 2: *p = (r_y << 3) | (g_u >> 3); break; case 1: - case 3: *p = (g_u << 5) | b_v; break; } @@ -385,27 +436,81 @@ static void gen_twopix(struct vivi_dev *dev, u8 *buf, int colorpos) case V4L2_PIX_FMT_RGB555: switch (color) { case 0: - case 2: *p = (g_u << 5) | b_v; break; case 1: - case 3: - *p = (r_y << 2) | (g_u >> 3); + *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); break; } break; case V4L2_PIX_FMT_RGB555X: switch (color) { case 0: - case 2: - *p = (r_y << 2) | (g_u >> 3); + *p = (alpha & 0x80) | (r_y << 2) | (g_u >> 3); break; case 1: - case 3: *p = (g_u << 5) | b_v; break; } break; + case V4L2_PIX_FMT_RGB24: + switch (color) { + case 0: + *p = r_y; + break; + case 1: + *p = g_u; + break; + case 2: + *p = b_v; + break; + } + break; + case V4L2_PIX_FMT_BGR24: + switch (color) { + case 0: + *p = b_v; + break; + case 1: + *p = g_u; + break; + case 2: + *p = r_y; + break; + } + break; + case V4L2_PIX_FMT_RGB32: + switch (color) { + case 0: + *p = alpha; + break; + case 1: + *p = r_y; + break; + case 2: + *p = g_u; + break; + case 3: + *p = b_v; + break; + } + break; + case V4L2_PIX_FMT_BGR32: + switch (color) { + case 0: + *p = b_v; + break; + case 1: + *p = g_u; + break; + case 2: + *p = r_y; + break; + case 3: + *p = alpha; + break; + } + break; } } } @@ -414,10 +519,10 @@ static void precalculate_line(struct vivi_dev *dev) { int w; - for (w = 0; w < dev->width * 2; w += 2) { - int colorpos = (w / (dev->width / 8) % 8); + for (w = 0; w < dev->width * 2; w++) { + int colorpos = w / (dev->width / 8) % 8; - gen_twopix(dev, dev->line + w * 2, colorpos); + gen_twopix(dev, dev->line + w * dev->pixelsize, colorpos, w & 1); } } @@ -433,7 +538,7 @@ static void gen_text(struct vivi_dev *dev, char *basep, /* Print stream time */ for (line = y; line < y + 16; line++) { int j = 0; - char *pos = basep + line * dev->width * 2 + x * 2; + char *pos = basep + line * dev->width * dev->pixelsize + x * dev->pixelsize; char *s; for (s = text; *s; s++) { @@ -443,9 +548,9 @@ static void gen_text(struct vivi_dev *dev, char *basep, for (i = 0; i < 7; i++, j++) { /* Draw white font on black background */ if (chr & (1 << (7 - i))) - gen_twopix(dev, pos + j * 2, WHITE); + gen_twopix(dev, pos + j * dev->pixelsize, WHITE, (x+y) & 1); else - gen_twopix(dev, pos + j * 2, TEXT_BLACK); + gen_twopix(dev, pos + j * dev->pixelsize, TEXT_BLACK, (x+y) & 1); } } } @@ -466,7 +571,9 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) return; for (h = 0; h < hmax; h++) - memcpy(vbuf + h * wmax * 2, dev->line + (dev->mv_count % wmax) * 2, wmax * 2); + memcpy(vbuf + h * wmax * dev->pixelsize, + dev->line + (dev->mv_count % wmax) * dev->pixelsize, + wmax * dev->pixelsize); /* Updates stream time */ @@ -484,15 +591,16 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) gen_text(dev, vbuf, line++ * 16, 16, str); gain = v4l2_ctrl_g_ctrl(dev->gain); - mutex_lock(&dev->ctrl_handler.lock); + mutex_lock(dev->ctrl_handler.lock); snprintf(str, sizeof(str), " brightness %3d, contrast %3d, saturation %3d, hue %d ", dev->brightness->cur.val, dev->contrast->cur.val, dev->saturation->cur.val, dev->hue->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); - snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d ", - dev->autogain->cur.val, gain, dev->volume->cur.val); + snprintf(str, sizeof(str), " autogain %d, gain %3d, volume %3d, alpha 0x%02x ", + dev->autogain->cur.val, gain, dev->volume->cur.val, + dev->alpha->cur.val); gen_text(dev, vbuf, line++ * 16, 16, str); snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ", dev->int32->cur.val, @@ -503,8 +611,12 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf) dev->boolean->cur.val, dev->menu->qmenu[dev->menu->cur.val], dev->string->cur.string); - mutex_unlock(&dev->ctrl_handler.lock); gen_text(dev, vbuf, line++ * 16, 16, str); + snprintf(str, sizeof(str), " integer_menu %lld, value %d ", + dev->int_menu->qmenu_int[dev->int_menu->cur.val], + dev->int_menu->cur.val); + gen_text(dev, vbuf, line++ * 16, 16, str); + mutex_unlock(dev->ctrl_handler.lock); if (dev->button_pressed) { dev->button_pressed--; snprintf(str, sizeof(str), " button pressed!"); @@ -657,7 +769,7 @@ static int queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt, struct vivi_dev *dev = vb2_get_drv_priv(vq); unsigned long size; - size = dev->width * dev->height * 2; + size = dev->width * dev->height * dev->pixelsize; if (0 == *nbuffers) *nbuffers = 32; @@ -721,7 +833,7 @@ static int buffer_prepare(struct vb2_buffer *vb) dev->height < 32 || dev->height > MAX_HEIGHT) return -EINVAL; - size = dev->width * dev->height * 2; + size = dev->width * dev->height * dev->pixelsize; if (vb2_plane_size(vb, 0) < size) { dprintk(dev, 1, "%s data will not fit into plane (%lu < %lu)\n", __func__, vb2_plane_size(vb, 0), size); @@ -915,6 +1027,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, } dev->fmt = get_format(f); + dev->pixelsize = dev->fmt->depth / 8; dev->width = f->fmt.pix.width; dev->height = f->fmt.pix.height; dev->field = f->fmt.pix.field; @@ -1016,8 +1129,15 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl) { struct vivi_dev *dev = container_of(ctrl->handler, struct vivi_dev, ctrl_handler); - if (ctrl == dev->button) - dev->button_pressed = 30; + switch (ctrl->id) { + case V4L2_CID_ALPHA_COMPONENT: + dev->alpha_component = ctrl->val; + break; + default: + if (ctrl == dev->button) + dev->button_pressed = 30; + break; + } return 0; } @@ -1039,17 +1159,10 @@ static unsigned int vivi_poll(struct file *file, struct poll_table_struct *wait) { struct vivi_dev *dev = video_drvdata(file); - struct v4l2_fh *fh = file->private_data; struct vb2_queue *q = &dev->vb_vidq; - unsigned int res; dprintk(dev, 1, "%s\n", __func__); - res = vb2_poll(q, file, wait); - if (v4l2_event_pending(fh)) - res |= POLLPRI; - else - poll_wait(file, &fh->wait, wait); - return res; + return vb2_poll(q, file, wait); } static int vivi_close(struct file *file) @@ -1165,6 +1278,22 @@ static const struct v4l2_ctrl_config vivi_ctrl_bitmask = { .step = 0, }; +static const s64 vivi_ctrl_int_menu_values[] = { + 1, 1, 2, 3, 5, 8, 13, 21, 42, +}; + +static const struct v4l2_ctrl_config vivi_ctrl_int_menu = { + .ops = &vivi_ctrl_ops, + .id = VIVI_CID_CUSTOM_BASE + 7, + .name = "Integer menu", + .type = V4L2_CTRL_TYPE_INTEGER_MENU, + .min = 1, + .max = 8, + .def = 4, + .menu_skip_mask = 0x02, + .qmenu_int = vivi_ctrl_int_menu_values, +}; + static const struct v4l2_file_operations vivi_fops = { .owner = THIS_MODULE, .open = v4l2_fh_open, @@ -1252,6 +1381,7 @@ static int __init vivi_create_instance(int inst) dev->fmt = &formats[0]; dev->width = 640; dev->height = 480; + dev->pixelsize = dev->fmt->depth / 8; hdl = &dev->ctrl_handler; v4l2_ctrl_handler_init(hdl, 11); dev->volume = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, @@ -1268,6 +1398,8 @@ static int __init vivi_create_instance(int inst) V4L2_CID_AUTOGAIN, 0, 1, 1, 1); dev->gain = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, V4L2_CID_GAIN, 0, 255, 1, 100); + dev->alpha = v4l2_ctrl_new_std(hdl, &vivi_ctrl_ops, + V4L2_CID_ALPHA_COMPONENT, 0, 255, 1, 0); dev->button = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_button, NULL); dev->int32 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int32, NULL); dev->int64 = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int64, NULL); @@ -1275,6 +1407,7 @@ static int __init vivi_create_instance(int inst) dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL); dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL); dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL); + dev->int_menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_int_menu, NULL); if (hdl->error) { ret = hdl->error; goto unreg_dev; diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c index 7fd7ac567e1a..db2a6003a1c3 100644 --- a/drivers/media/video/w9966.c +++ b/drivers/media/video/w9966.c @@ -62,6 +62,9 @@ #include #include #include +#include +#include +#include #include /*#define DEBUG*/ /* Undef me for production */ @@ -104,6 +107,7 @@ struct w9966 { struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler hdl; unsigned char dev_state; unsigned char i2c_state; unsigned short ppmode; @@ -567,7 +571,8 @@ static int cam_querycap(struct file *file, void *priv, strlcpy(vcap->driver, cam->v4l2_dev.name, sizeof(vcap->driver)); strlcpy(vcap->card, W9966_DRIVERNAME, sizeof(vcap->card)); strlcpy(vcap->bus_info, "parport", sizeof(vcap->bus_info)); - vcap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; + vcap->capabilities = vcap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; } @@ -595,67 +600,25 @@ static int cam_s_input(struct file *file, void *fh, unsigned int inp) return (inp > 0) ? -EINVAL : 0; } -static int cam_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) +static int cam_s_ctrl(struct v4l2_ctrl *ctrl) { - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(qc, -64, 64, 1, 64); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0); - } - return -EINVAL; -} - -static int cam_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct w9966 *cam = video_drvdata(file); - int ret = 0; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = cam->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = cam->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = cam->color; - break; - case V4L2_CID_HUE: - ctrl->value = cam->hue; - break; - default: - ret = -EINVAL; - break; - } - return ret; -} - -static int cam_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct w9966 *cam = video_drvdata(file); + struct w9966 *cam = + container_of(ctrl->handler, struct w9966, hdl); int ret = 0; mutex_lock(&cam->lock); switch (ctrl->id) { case V4L2_CID_BRIGHTNESS: - cam->brightness = ctrl->value; + cam->brightness = ctrl->val; break; case V4L2_CID_CONTRAST: - cam->contrast = ctrl->value; + cam->contrast = ctrl->val; break; case V4L2_CID_SATURATION: - cam->color = ctrl->value; + cam->color = ctrl->val; break; case V4L2_CID_HUE: - cam->hue = ctrl->value; + cam->hue = ctrl->val; break; default: ret = -EINVAL; @@ -813,6 +776,9 @@ out: static const struct v4l2_file_operations w9966_fops = { .owner = THIS_MODULE, + .open = v4l2_fh_open, + .release = v4l2_fh_release, + .poll = v4l2_ctrl_poll, .unlocked_ioctl = video_ioctl2, .read = w9966_v4l_read, }; @@ -822,13 +788,17 @@ static const struct v4l2_ioctl_ops w9966_ioctl_ops = { .vidioc_g_input = cam_g_input, .vidioc_s_input = cam_s_input, .vidioc_enum_input = cam_enum_input, - .vidioc_queryctrl = cam_queryctrl, - .vidioc_g_ctrl = cam_g_ctrl, - .vidioc_s_ctrl = cam_s_ctrl, .vidioc_enum_fmt_vid_cap = cam_enum_fmt_vid_cap, .vidioc_g_fmt_vid_cap = cam_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = cam_s_fmt_vid_cap, .vidioc_try_fmt_vid_cap = cam_try_fmt_vid_cap, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static const struct v4l2_ctrl_ops cam_ctrl_ops = { + .s_ctrl = cam_s_ctrl, }; @@ -849,6 +819,20 @@ static int w9966_init(struct w9966 *cam, struct parport *port) v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); return -1; } + + v4l2_ctrl_handler_init(&cam->hdl, 4); + v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 255, 1, 128); + v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, + V4L2_CID_CONTRAST, -64, 64, 1, 64); + v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, + V4L2_CID_SATURATION, -64, 64, 1, 64); + v4l2_ctrl_new_std(&cam->hdl, &cam_ctrl_ops, + V4L2_CID_HUE, -128, 127, 1, 0); + if (cam->hdl.error) { + v4l2_err(v4l2_dev, "couldn't register controls\n"); + return -1; + } cam->pport = port; cam->brightness = 128; cam->contrast = 64; @@ -898,6 +882,8 @@ static int w9966_init(struct w9966 *cam, struct parport *port) cam->vdev.fops = &w9966_fops; cam->vdev.ioctl_ops = &w9966_ioctl_ops; cam->vdev.release = video_device_release_empty; + cam->vdev.ctrl_handler = &cam->hdl; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); video_set_drvdata(&cam->vdev, cam); mutex_init(&cam->lock); @@ -923,6 +909,8 @@ static void w9966_term(struct w9966 *cam) w9966_set_state(cam, W9966_STATE_VDEV, 0); } + v4l2_ctrl_handler_free(&cam->hdl); + /* Terminate from IEEE1284 mode and release pdev block */ if (w9966_get_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV)) { w9966_pdev_claim(cam); diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c index e86173bd1327..a4cd504b8eee 100644 --- a/drivers/media/video/zoran/zoran_device.c +++ b/drivers/media/video/zoran/zoran_device.c @@ -542,11 +542,9 @@ void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count) u32 *mask; int x, y, width, height; unsigned i, j, k; - u32 reg; /* fill mask with one bits */ memset(fh->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT); - reg = 0; for (i = 0; i < count; ++i) { /* pick up local copy of clip */ diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c index 4c09ab781ec3..c57310931810 100644 --- a/drivers/media/video/zoran/zoran_driver.c +++ b/drivers/media/video/zoran/zoran_driver.c @@ -1131,8 +1131,14 @@ static int setup_fbuffer(struct zoran_fh *fh, } -static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height, - struct v4l2_clip __user *clips, int clipcount, void __user *bitmap) +static int setup_window(struct zoran_fh *fh, + int x, + int y, + int width, + int height, + struct v4l2_clip __user *clips, + unsigned int clipcount, + void __user *bitmap) { struct zoran *zr = fh->zr; struct v4l2_clip *vcp = NULL; @@ -1155,6 +1161,14 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height return -EINVAL; } + if (clipcount > 2048) { + dprintk(1, + KERN_ERR + "%s: %s - invalid clipcount\n", + ZR_DEVNAME(zr), __func__); + return -EINVAL; + } + /* * The video front end needs 4-byte alinged line sizes, we correct that * silently here if necessary @@ -1218,7 +1232,7 @@ static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height (width * height + 7) / 8)) { return -EFAULT; } - } else if (clipcount > 0) { + } else if (clipcount) { /* write our own bitmap from the clips */ vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4)); if (vcp == NULL) { diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c index cd2e39fc4bf0..e44cb330bbc8 100644 --- a/drivers/media/video/zr364xx.c +++ b/drivers/media/video/zr364xx.c @@ -507,14 +507,12 @@ static void zr364xx_fillbuff(struct zr364xx_camera *cam, const char *tmpbuf; char *vbuf = videobuf_to_vmalloc(&buf->vb); unsigned long last_frame; - struct zr364xx_framei *frm; if (!vbuf) return; last_frame = cam->last_frame; if (last_frame != -1) { - frm = &cam->buffer.frame[last_frame]; tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; switch (buf->fmt->fourcc) { case V4L2_PIX_FMT_JPEG: diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index 9f1f27e7c86e..4511420849bc 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -269,7 +269,7 @@ out: return ret; } -static inline unsigned long calc_vm_may_flags(unsigned long prot) +static inline vm_flags_t calc_vm_may_flags(unsigned long prot) { return _calc_vm_trans(prot, PROT_READ, VM_MAYREAD) | _calc_vm_trans(prot, PROT_WRITE, VM_MAYWRITE) | diff --git a/drivers/staging/media/as102/as10x_cmd.c b/drivers/staging/media/as102/as10x_cmd.c index 262bb94ad27e..a73df10982d0 100644 --- a/drivers/staging/media/as102/as10x_cmd.c +++ b/drivers/staging/media/as102/as10x_cmd.c @@ -31,7 +31,7 @@ */ int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -54,8 +54,6 @@ int as10x_cmd_turn_on(struct as10x_bus_adapter_t *adap) (uint8_t *) prsp, sizeof(prsp->body.turn_on.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -77,7 +75,7 @@ out: */ int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -99,8 +97,6 @@ int as10x_cmd_turn_off(struct as10x_bus_adapter_t *adap) sizeof(pcmd->body.turn_off.req) + HEADER_SIZE, (uint8_t *) prsp, sizeof(prsp->body.turn_off.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -124,7 +120,7 @@ out: int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, struct as10x_tune_args *ptune) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *preq, *prsp; ENTER(); @@ -159,8 +155,6 @@ int as10x_cmd_set_tune(struct as10x_bus_adapter_t *adap, (uint8_t *) prsp, sizeof(prsp->body.set_tune.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -184,7 +178,7 @@ out: int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, struct as10x_tune_status *pstatus) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *preq, *prsp; ENTER(); @@ -208,8 +202,6 @@ int as10x_cmd_get_tune_status(struct as10x_bus_adapter_t *adap, sizeof(preq->body.get_tune_status.req) + HEADER_SIZE, (uint8_t *) prsp, sizeof(prsp->body.get_tune_status.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -241,7 +233,7 @@ out: */ int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -266,8 +258,6 @@ int as10x_cmd_get_tps(struct as10x_bus_adapter_t *adap, struct as10x_tps *ptps) (uint8_t *) prsp, sizeof(prsp->body.get_tps.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -305,7 +295,7 @@ out: int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, struct as10x_demod_stats *pdemod_stats) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -330,8 +320,6 @@ int as10x_cmd_get_demod_stats(struct as10x_bus_adapter_t *adap, (uint8_t *) prsp, sizeof(prsp->body.get_demod_stats.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) @@ -370,7 +358,7 @@ out: int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, uint8_t *is_ready) { - int error; + int error = AS10X_CMD_ERROR; struct as10x_cmd_t *pcmd, *prsp; ENTER(); @@ -395,8 +383,6 @@ int as10x_cmd_get_impulse_resp(struct as10x_bus_adapter_t *adap, (uint8_t *) prsp, sizeof(prsp->body.get_impulse_rsp.rsp) + HEADER_SIZE); - } else { - error = AS10X_CMD_ERROR; } if (error < 0) diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c index 280c84ec4cc2..c365cdf714ea 100644 --- a/drivers/staging/media/dt3155v4l/dt3155v4l.c +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -898,6 +898,10 @@ dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&pd->dmaq); mutex_init(&pd->mux); pd->vdev->lock = &pd->mux; /* for locking v4l2_file_operations */ + /* Locking in file operations other than ioctl should be done + by the driver, not the V4L2 core. + This driver needs auditing so that this flag can be removed. */ + set_bit(V4L2_FL_LOCK_ALL_FOPS, &pd->vdev->flags); spin_lock_init(&pd->lock); pd->csr2 = csr2_init; pd->config = config_init; diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c index 6f83d362ab0d..a1c45e4dcdce 100644 --- a/drivers/staging/media/easycap/easycap_main.c +++ b/drivers/staging/media/easycap/easycap_main.c @@ -700,214 +700,7 @@ static int videodev_release(struct video_device *pvideo_device) JOM(4, "ending successfully\n"); return 0; } -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS - * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect(). - * - * BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO - * peasycap->pusb_device IS NO LONGER VALID. - */ -/*---------------------------------------------------------------------------*/ -static void easycap_delete(struct kref *pkref) -{ - struct easycap *peasycap; - struct data_urb *pdata_urb; - struct list_head *plist_head, *plist_next; - int k, m, gone, kd; - int allocation_video_urb; - int allocation_video_page; - int allocation_video_struct; - int allocation_audio_urb; - int allocation_audio_page; - int allocation_audio_struct; - int registered_video, registered_audio; - peasycap = container_of(pkref, struct easycap, kref); - if (!peasycap) { - SAM("ERROR: peasycap is NULL: cannot perform deletions\n"); - return; - } - kd = easycap_isdongle(peasycap); -/*---------------------------------------------------------------------------*/ -/* - * FREE VIDEO. - */ -/*---------------------------------------------------------------------------*/ - if (peasycap->purb_video_head) { - m = 0; - list_for_each(plist_head, peasycap->purb_video_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) { - usb_free_urb(pdata_urb->purb); - pdata_urb->purb = NULL; - peasycap->allocation_video_urb--; - m++; - } - } - - JOM(4, "%i video urbs freed\n", m); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing video data_urb structures.\n"); - m = 0; - list_for_each_safe(plist_head, plist_next, - peasycap->purb_video_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - peasycap->allocation_video_struct -= - sizeof(struct data_urb); - kfree(pdata_urb); - m++; - } - } - JOM(4, "%i video data_urb structures freed\n", m); - JOM(4, "setting peasycap->purb_video_head=NULL\n"); - peasycap->purb_video_head = NULL; - } -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing video isoc buffers.\n"); - m = 0; - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - if (peasycap->video_isoc_buffer[k].pgo) { - free_pages((unsigned long) - peasycap->video_isoc_buffer[k].pgo, - VIDEO_ISOC_ORDER); - peasycap->video_isoc_buffer[k].pgo = NULL; - peasycap->allocation_video_page -= - BIT(VIDEO_ISOC_ORDER); - m++; - } - } - JOM(4, "isoc video buffers freed: %i pages\n", - m * (0x01 << VIDEO_ISOC_ORDER)); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing video field buffers.\n"); - gone = 0; - for (k = 0; k < FIELD_BUFFER_MANY; k++) { - for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->field_buffer[k][m].pgo) { - free_page((unsigned long) - peasycap->field_buffer[k][m].pgo); - peasycap->field_buffer[k][m].pgo = NULL; - peasycap->allocation_video_page -= 1; - gone++; - } - } - } - JOM(4, "video field buffers freed: %i pages\n", gone); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing video frame buffers.\n"); - gone = 0; - for (k = 0; k < FRAME_BUFFER_MANY; k++) { - for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->frame_buffer[k][m].pgo) { - free_page((unsigned long) - peasycap->frame_buffer[k][m].pgo); - peasycap->frame_buffer[k][m].pgo = NULL; - peasycap->allocation_video_page -= 1; - gone++; - } - } - } - JOM(4, "video frame buffers freed: %i pages\n", gone); -/*---------------------------------------------------------------------------*/ -/* - * FREE AUDIO. - */ -/*---------------------------------------------------------------------------*/ - if (peasycap->purb_audio_head) { - JOM(4, "freeing audio urbs\n"); - m = 0; - list_for_each(plist_head, (peasycap->purb_audio_head)) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) { - usb_free_urb(pdata_urb->purb); - pdata_urb->purb = NULL; - peasycap->allocation_audio_urb--; - m++; - } - } - JOM(4, "%i audio urbs freed\n", m); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing audio data_urb structures.\n"); - m = 0; - list_for_each_safe(plist_head, plist_next, - peasycap->purb_audio_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - peasycap->allocation_audio_struct -= - sizeof(struct data_urb); - kfree(pdata_urb); - m++; - } - } - JOM(4, "%i audio data_urb structures freed\n", m); - JOM(4, "setting peasycap->purb_audio_head=NULL\n"); - peasycap->purb_audio_head = NULL; - } -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing audio isoc buffers.\n"); - m = 0; - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - if (peasycap->audio_isoc_buffer[k].pgo) { - free_pages((unsigned long) - (peasycap->audio_isoc_buffer[k].pgo), - AUDIO_ISOC_ORDER); - peasycap->audio_isoc_buffer[k].pgo = NULL; - peasycap->allocation_audio_page -= - BIT(AUDIO_ISOC_ORDER); - m++; - } - } - JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n", - m * (0x01 << AUDIO_ISOC_ORDER)); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing easycap structure.\n"); - allocation_video_urb = peasycap->allocation_video_urb; - allocation_video_page = peasycap->allocation_video_page; - allocation_video_struct = peasycap->allocation_video_struct; - registered_video = peasycap->registered_video; - allocation_audio_urb = peasycap->allocation_audio_urb; - allocation_audio_page = peasycap->allocation_audio_page; - allocation_audio_struct = peasycap->allocation_audio_struct; - registered_audio = peasycap->registered_audio; - - if (0 <= kd && DONGLE_MANY > kd) { - if (mutex_lock_interruptible(&mutex_dongle)) { - SAY("ERROR: cannot down mutex_dongle\n"); - } else { - JOM(4, "locked mutex_dongle\n"); - easycapdc60_dongle[kd].peasycap = NULL; - mutex_unlock(&mutex_dongle); - JOM(4, "unlocked mutex_dongle\n"); - JOT(4, " null-->dongle[%i].peasycap\n", kd); - allocation_video_struct -= sizeof(struct easycap); - } - } else { - SAY("ERROR: cannot purge dongle[].peasycap"); - } - - kfree(peasycap); - -/*---------------------------------------------------------------------------*/ - SAY("%8i=video urbs after all deletions\n", allocation_video_urb); - SAY("%8i=video pages after all deletions\n", allocation_video_page); - SAY("%8i=video structs after all deletions\n", allocation_video_struct); - SAY("%8i=video devices after all deletions\n", registered_video); - SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb); - SAY("%8i=audio pages after all deletions\n", allocation_audio_page); - SAY("%8i=audio structs after all deletions\n", allocation_audio_struct); - SAY("%8i=audio devices after all deletions\n", registered_audio); - - JOT(4, "ending.\n"); - return; -} /*****************************************************************************/ static unsigned int easycap_poll(struct file *file, poll_table *wait) { @@ -2842,6 +2635,813 @@ static void easycap_complete(struct urb *purb) return; } +static struct easycap *alloc_easycap(u8 bInterfaceNumber) +{ + struct easycap *peasycap; + int i; + + peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL); + if (!peasycap) { + SAY("ERROR: Could not allocate peasycap\n"); + return NULL; + } + + if (mutex_lock_interruptible(&mutex_dongle)) { + SAY("ERROR: cannot lock mutex_dongle\n"); + kfree(peasycap); + return NULL; + } + + /* Find a free dongle in easycapdc60_dongle array */ + for (i = 0; i < DONGLE_MANY; i++) { + + if ((!easycapdc60_dongle[i].peasycap) && + (!mutex_is_locked(&easycapdc60_dongle[i].mutex_video)) && + (!mutex_is_locked(&easycapdc60_dongle[i].mutex_audio))) { + + easycapdc60_dongle[i].peasycap = peasycap; + peasycap->isdongle = i; + JOM(8, "intf[%i]: peasycap-->easycap" + "_dongle[%i].peasycap\n", + bInterfaceNumber, i); + break; + } + } + + mutex_unlock(&mutex_dongle); + + if (i >= DONGLE_MANY) { + SAM("ERROR: too many dongles\n"); + kfree(peasycap); + return NULL; + } + + return peasycap; +} + +static void free_easycap(struct easycap *peasycap) +{ + int allocation_video_urb; + int allocation_video_page; + int allocation_video_struct; + int allocation_audio_urb; + int allocation_audio_page; + int allocation_audio_struct; + int registered_video, registered_audio; + int kd; + + JOM(4, "freeing easycap structure.\n"); + allocation_video_urb = peasycap->allocation_video_urb; + allocation_video_page = peasycap->allocation_video_page; + allocation_video_struct = peasycap->allocation_video_struct; + registered_video = peasycap->registered_video; + allocation_audio_urb = peasycap->allocation_audio_urb; + allocation_audio_page = peasycap->allocation_audio_page; + allocation_audio_struct = peasycap->allocation_audio_struct; + registered_audio = peasycap->registered_audio; + + kd = easycap_isdongle(peasycap); + if (0 <= kd && DONGLE_MANY > kd) { + if (mutex_lock_interruptible(&mutex_dongle)) { + SAY("ERROR: cannot down mutex_dongle\n"); + } else { + JOM(4, "locked mutex_dongle\n"); + easycapdc60_dongle[kd].peasycap = NULL; + mutex_unlock(&mutex_dongle); + JOM(4, "unlocked mutex_dongle\n"); + JOT(4, " null-->dongle[%i].peasycap\n", kd); + allocation_video_struct -= sizeof(struct easycap); + } + } else { + SAY("ERROR: cannot purge dongle[].peasycap"); + } + + /* Free device structure */ + kfree(peasycap); + + SAY("%8i=video urbs after all deletions\n", allocation_video_urb); + SAY("%8i=video pages after all deletions\n", allocation_video_page); + SAY("%8i=video structs after all deletions\n", allocation_video_struct); + SAY("%8i=video devices after all deletions\n", registered_video); + SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb); + SAY("%8i=audio pages after all deletions\n", allocation_audio_page); + SAY("%8i=audio structs after all deletions\n", allocation_audio_struct); + SAY("%8i=audio devices after all deletions\n", registered_audio); +} + +/* + * FIXME: Identify the appropriate pointer peasycap for interfaces + * 1 and 2. The address of peasycap->pusb_device is reluctantly used + * for this purpose. + */ +static struct easycap *get_easycap(struct usb_device *usbdev, + u8 bInterfaceNumber) +{ + int i; + struct easycap *peasycap; + + for (i = 0; i < DONGLE_MANY; i++) { + if (easycapdc60_dongle[i].peasycap->pusb_device == usbdev) { + peasycap = easycapdc60_dongle[i].peasycap; + JOT(8, "intf[%i]: dongle[%i].peasycap\n", + bInterfaceNumber, i); + break; + } + } + if (i >= DONGLE_MANY) { + SAY("ERROR: peasycap is unknown when probing interface %i\n", + bInterfaceNumber); + return NULL; + } + if (!peasycap) { + SAY("ERROR: peasycap is NULL when probing interface %i\n", + bInterfaceNumber); + return NULL; + } + + return peasycap; +} + +static void init_easycap(struct easycap *peasycap, + struct usb_device *usbdev, + struct usb_interface *intf, + u8 bInterfaceNumber) +{ + /* Save usb_device and usb_interface */ + peasycap->pusb_device = usbdev; + peasycap->pusb_interface = intf; + + peasycap->minor = -1; + kref_init(&peasycap->kref); + JOM(8, "intf[%i]: after kref_init(..._video) " + "%i=peasycap->kref.refcount.counter\n", + bInterfaceNumber, peasycap->kref.refcount.counter); + + /* module params */ + peasycap->gain = (s8)clamp(easycap_gain, 0, 31); + + init_waitqueue_head(&peasycap->wq_video); + init_waitqueue_head(&peasycap->wq_audio); + init_waitqueue_head(&peasycap->wq_trigger); + + peasycap->allocation_video_struct = sizeof(struct easycap); + + peasycap->microphone = false; + + peasycap->video_interface = -1; + peasycap->video_altsetting_on = -1; + peasycap->video_altsetting_off = -1; + peasycap->video_endpointnumber = -1; + peasycap->video_isoc_maxframesize = -1; + peasycap->video_isoc_buffer_size = -1; + + peasycap->audio_interface = -1; + peasycap->audio_altsetting_on = -1; + peasycap->audio_altsetting_off = -1; + peasycap->audio_endpointnumber = -1; + peasycap->audio_isoc_maxframesize = -1; + peasycap->audio_isoc_buffer_size = -1; + + peasycap->frame_buffer_many = FRAME_BUFFER_MANY; + + peasycap->ntsc = easycap_ntsc; + JOM(8, "defaulting initially to %s\n", + easycap_ntsc ? "NTSC" : "PAL"); +} + +static int populate_inputset(struct easycap *peasycap) +{ + struct inputset *inputset; + struct easycap_format *peasycap_format; + struct v4l2_pix_format *pix; + int m, i, k, mask, fmtidx; + s32 value; + + inputset = peasycap->inputset; + + fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; + + m = 0; + mask = 0; + for (i = 0; easycap_standard[i].mask != 0xffff; i++) { + if (fmtidx == easycap_standard[i].v4l2_standard.index) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].standard_offset = i; + mask = easycap_standard[i].mask; + } + } + + if (m != 1) { + SAM("ERROR: inputset->standard_offset unpopulated, %i=m\n", m); + return -ENOENT; + } + + peasycap_format = &easycap_format[0]; + m = 0; + for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) { + pix = &peasycap_format->v4l2_format.fmt.pix; + if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) + && pix->field == V4L2_FIELD_NONE + && pix->pixelformat == V4L2_PIX_FMT_UYVY + && pix->width == 640 && pix->height == 480) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].format_offset = i; + break; + } + peasycap_format++; + } + if (m != 1) { + SAM("ERROR: inputset[]->format_offset unpopulated\n"); + return -ENOENT; + } + + m = 0; + for (i = 0; easycap_control[i].id != 0xffffffff; i++) { + value = easycap_control[i].default_value; + if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].brightness = value; + } else if (V4L2_CID_CONTRAST == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].contrast = value; + } else if (V4L2_CID_SATURATION == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].saturation = value; + } else if (V4L2_CID_HUE == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].hue = value; + } + } + + if (m != 4) { + SAM("ERROR: inputset[]->brightness underpopulated\n"); + return -ENOENT; + } + + for (k = 0; k < INPUT_MANY; k++) + inputset[k].input = k; + JOM(4, "populated inputset[]\n"); + + return 0; +} + +static int alloc_framebuffers(struct easycap *peasycap) +{ + int i, j; + void *pbuf; + + JOM(4, "allocating %i frame buffers of size %li\n", + FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE); + JOM(4, ".... each scattered over %li pages\n", + FRAME_BUFFER_SIZE/PAGE_SIZE); + + for (i = 0; i < FRAME_BUFFER_MANY; i++) { + for (j = 0; j < FRAME_BUFFER_SIZE/PAGE_SIZE; j++) { + if (peasycap->frame_buffer[i][j].pgo) + SAM("attempting to reallocate framebuffers\n"); + else { + pbuf = (void *)__get_free_page(GFP_KERNEL); + if (!pbuf) { + SAM("ERROR: Could not allocate " + "framebuffer %i page %i\n", i, j); + return -ENOMEM; + } + peasycap->allocation_video_page += 1; + peasycap->frame_buffer[i][j].pgo = pbuf; + } + peasycap->frame_buffer[i][j].pto = + peasycap->frame_buffer[i][j].pgo; + } + } + + peasycap->frame_fill = 0; + peasycap->frame_read = 0; + JOM(4, "allocation of frame buffers done: %i pages\n", i*j); + + return 0; +} + +static void free_framebuffers(struct easycap *peasycap) +{ + int k, m, gone; + + JOM(4, "freeing video frame buffers.\n"); + gone = 0; + for (k = 0; k < FRAME_BUFFER_MANY; k++) { + for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { + if (peasycap->frame_buffer[k][m].pgo) { + free_page((unsigned long) + peasycap->frame_buffer[k][m].pgo); + peasycap->frame_buffer[k][m].pgo = NULL; + peasycap->allocation_video_page -= 1; + gone++; + } + } + } + JOM(4, "video frame buffers freed: %i pages\n", gone); +} + +static int alloc_fieldbuffers(struct easycap *peasycap) +{ + int i, j; + void *pbuf; + + JOM(4, "allocating %i field buffers of size %li\n", + FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE); + JOM(4, ".... each scattered over %li pages\n", + FIELD_BUFFER_SIZE/PAGE_SIZE); + + for (i = 0; i < FIELD_BUFFER_MANY; i++) { + for (j = 0; j < FIELD_BUFFER_SIZE/PAGE_SIZE; j++) { + if (peasycap->field_buffer[i][j].pgo) { + SAM("ERROR: attempting to reallocate " + "fieldbuffers\n"); + } else { + pbuf = (void *) __get_free_page(GFP_KERNEL); + if (!pbuf) { + SAM("ERROR: Could not allocate " + "fieldbuffer %i page %i\n", i, j); + return -ENOMEM; + } + peasycap->allocation_video_page += 1; + peasycap->field_buffer[i][j].pgo = pbuf; + } + peasycap->field_buffer[i][j].pto = + peasycap->field_buffer[i][j].pgo; + } + /* TODO: Hardcoded 0x0200 meaning? */ + peasycap->field_buffer[i][0].kount = 0x0200; + } + peasycap->field_fill = 0; + peasycap->field_page = 0; + peasycap->field_read = 0; + JOM(4, "allocation of field buffers done: %i pages\n", i*j); + + return 0; +} + +static void free_fieldbuffers(struct easycap *peasycap) +{ + int k, m, gone; + + JOM(4, "freeing video field buffers.\n"); + gone = 0; + for (k = 0; k < FIELD_BUFFER_MANY; k++) { + for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { + if (peasycap->field_buffer[k][m].pgo) { + free_page((unsigned long) + peasycap->field_buffer[k][m].pgo); + peasycap->field_buffer[k][m].pgo = NULL; + peasycap->allocation_video_page -= 1; + gone++; + } + } + } + JOM(4, "video field buffers freed: %i pages\n", gone); +} + +static int alloc_isocbuffers(struct easycap *peasycap) +{ + int i; + void *pbuf; + + JOM(4, "allocating %i isoc video buffers of size %i\n", + VIDEO_ISOC_BUFFER_MANY, + peasycap->video_isoc_buffer_size); + JOM(4, ".... each occupying contiguous memory pages\n"); + + for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) { + pbuf = (void *)__get_free_pages(GFP_KERNEL, + VIDEO_ISOC_ORDER); + if (!pbuf) { + SAM("ERROR: Could not allocate isoc " + "video buffer %i\n", i); + return -ENOMEM; + } + peasycap->allocation_video_page += BIT(VIDEO_ISOC_ORDER); + + peasycap->video_isoc_buffer[i].pgo = pbuf; + peasycap->video_isoc_buffer[i].pto = + pbuf + peasycap->video_isoc_buffer_size; + peasycap->video_isoc_buffer[i].kount = i; + } + JOM(4, "allocation of isoc video buffers done: %i pages\n", + i * (0x01 << VIDEO_ISOC_ORDER)); + return 0; +} + +static void free_isocbuffers(struct easycap *peasycap) +{ + int k, m; + + JOM(4, "freeing video isoc buffers.\n"); + m = 0; + for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { + if (peasycap->video_isoc_buffer[k].pgo) { + free_pages((unsigned long) + peasycap->video_isoc_buffer[k].pgo, + VIDEO_ISOC_ORDER); + peasycap->video_isoc_buffer[k].pgo = NULL; + peasycap->allocation_video_page -= + BIT(VIDEO_ISOC_ORDER); + m++; + } + } + JOM(4, "isoc video buffers freed: %i pages\n", + m * (0x01 << VIDEO_ISOC_ORDER)); +} + +static int create_video_urbs(struct easycap *peasycap) +{ + struct urb *purb; + struct data_urb *pdata_urb; + int i, j; + + JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY); + JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n", + peasycap->video_isoc_framesperdesc); + JOM(4, "using %i=peasycap->video_isoc_maxframesize\n", + peasycap->video_isoc_maxframesize); + JOM(4, "using %i=peasycap->video_isoc_buffer_sizen", + peasycap->video_isoc_buffer_size); + + for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) { + purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc, + GFP_KERNEL); + if (!purb) { + SAM("ERROR: usb_alloc_urb returned NULL for buffer " + "%i\n", i); + return -ENOMEM; + } + + peasycap->allocation_video_urb += 1; + pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); + if (!pdata_urb) { + SAM("ERROR: Could not allocate struct data_urb.\n"); + return -ENOMEM; + } + + peasycap->allocation_video_struct += + sizeof(struct data_urb); + + pdata_urb->purb = purb; + pdata_urb->isbuf = i; + pdata_urb->length = 0; + list_add_tail(&(pdata_urb->list_head), + peasycap->purb_video_head); + + if (!i) { + JOM(4, "initializing video urbs thus:\n"); + JOM(4, " purb->interval = 1;\n"); + JOM(4, " purb->dev = peasycap->pusb_device;\n"); + JOM(4, " purb->pipe = usb_rcvisocpipe" + "(peasycap->pusb_device,%i);\n", + peasycap->video_endpointnumber); + JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); + JOM(4, " purb->transfer_buffer = peasycap->" + "video_isoc_buffer[.].pgo;\n"); + JOM(4, " purb->transfer_buffer_length = %i;\n", + peasycap->video_isoc_buffer_size); + JOM(4, " purb->complete = easycap_complete;\n"); + JOM(4, " purb->context = peasycap;\n"); + JOM(4, " purb->start_frame = 0;\n"); + JOM(4, " purb->number_of_packets = %i;\n", + peasycap->video_isoc_framesperdesc); + JOM(4, " for (j = 0; j < %i; j++)\n", + peasycap->video_isoc_framesperdesc); + JOM(4, " {\n"); + JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", + peasycap->video_isoc_maxframesize); + JOM(4, " purb->iso_frame_desc[j].length = %i;\n", + peasycap->video_isoc_maxframesize); + JOM(4, " }\n"); + } + + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, + peasycap->video_endpointnumber); + + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = peasycap->video_isoc_buffer[i].pgo; + purb->transfer_buffer_length = + peasycap->video_isoc_buffer_size; + + purb->complete = easycap_complete; + purb->context = peasycap; + purb->start_frame = 0; + purb->number_of_packets = peasycap->video_isoc_framesperdesc; + + for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j].offset = + j * peasycap->video_isoc_maxframesize; + purb->iso_frame_desc[j].length = + peasycap->video_isoc_maxframesize; + } + } + JOM(4, "allocation of %i struct urb done.\n", i); + return 0; +} + +static void free_video_urbs(struct easycap *peasycap) +{ + struct list_head *plist_head, *plist_next; + struct data_urb *pdata_urb; + int m; + + if (peasycap->purb_video_head) { + m = 0; + list_for_each(plist_head, peasycap->purb_video_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + usb_free_urb(pdata_urb->purb); + pdata_urb->purb = NULL; + peasycap->allocation_video_urb--; + m++; + } + } + + JOM(4, "%i video urbs freed\n", m); + JOM(4, "freeing video data_urb structures.\n"); + m = 0; + list_for_each_safe(plist_head, plist_next, + peasycap->purb_video_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + peasycap->allocation_video_struct -= + sizeof(struct data_urb); + kfree(pdata_urb); + m++; + } + } + JOM(4, "%i video data_urb structures freed\n", m); + JOM(4, "setting peasycap->purb_video_head=NULL\n"); + peasycap->purb_video_head = NULL; + } +} + +static int alloc_audio_buffers(struct easycap *peasycap) +{ + void *pbuf; + int k; + + JOM(4, "allocating %i isoc audio buffers of size %i\n", + AUDIO_ISOC_BUFFER_MANY, + peasycap->audio_isoc_buffer_size); + JOM(4, ".... each occupying contiguous memory pages\n"); + + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + pbuf = (void *)__get_free_pages(GFP_KERNEL, AUDIO_ISOC_ORDER); + if (!pbuf) { + SAM("ERROR: Could not allocate isoc audio buffer %i\n", + k); + return -ENOMEM; + } + peasycap->allocation_audio_page += BIT(AUDIO_ISOC_ORDER); + + peasycap->audio_isoc_buffer[k].pgo = pbuf; + peasycap->audio_isoc_buffer[k].pto = + pbuf + peasycap->audio_isoc_buffer_size; + peasycap->audio_isoc_buffer[k].kount = k; + } + + JOM(4, "allocation of isoc audio buffers done.\n"); + return 0; +} + +static void free_audio_buffers(struct easycap *peasycap) +{ + int k, m; + + JOM(4, "freeing audio isoc buffers.\n"); + m = 0; + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + if (peasycap->audio_isoc_buffer[k].pgo) { + free_pages((unsigned long) + (peasycap->audio_isoc_buffer[k].pgo), + AUDIO_ISOC_ORDER); + peasycap->audio_isoc_buffer[k].pgo = NULL; + peasycap->allocation_audio_page -= + BIT(AUDIO_ISOC_ORDER); + m++; + } + } + JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n", + m * (0x01 << AUDIO_ISOC_ORDER)); +} + +static int create_audio_urbs(struct easycap *peasycap) +{ + struct urb *purb; + struct data_urb *pdata_urb; + int k, j; + + JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY); + JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n", + peasycap->audio_isoc_maxframesize); + JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n", + peasycap->audio_isoc_buffer_size); + + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc, + GFP_KERNEL); + if (!purb) { + SAM("ERROR: usb_alloc_urb returned NULL for buffer " + "%i\n", k); + return -ENOMEM; + } + peasycap->allocation_audio_urb += 1 ; + pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); + if (!pdata_urb) { + usb_free_urb(purb); + SAM("ERROR: Could not allocate struct data_urb.\n"); + return -ENOMEM; + } + peasycap->allocation_audio_struct += + sizeof(struct data_urb); + + pdata_urb->purb = purb; + pdata_urb->isbuf = k; + pdata_urb->length = 0; + list_add_tail(&(pdata_urb->list_head), + peasycap->purb_audio_head); + + if (!k) { + JOM(4, "initializing audio urbs thus:\n"); + JOM(4, " purb->interval = 1;\n"); + JOM(4, " purb->dev = peasycap->pusb_device;\n"); + JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->" + "pusb_device,%i);\n", + peasycap->audio_endpointnumber); + JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); + JOM(4, " purb->transfer_buffer = " + "peasycap->audio_isoc_buffer[.].pgo;\n"); + JOM(4, " purb->transfer_buffer_length = %i;\n", + peasycap->audio_isoc_buffer_size); + JOM(4, " purb->complete = easycap_alsa_complete;\n"); + JOM(4, " purb->context = peasycap;\n"); + JOM(4, " purb->start_frame = 0;\n"); + JOM(4, " purb->number_of_packets = %i;\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, " for (j = 0; j < %i; j++)\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, " {\n"); + JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", + peasycap->audio_isoc_maxframesize); + JOM(4, " purb->iso_frame_desc[j].length = %i;\n", + peasycap->audio_isoc_maxframesize); + JOM(4, " }\n"); + } + + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, + peasycap->audio_endpointnumber); + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo; + purb->transfer_buffer_length = + peasycap->audio_isoc_buffer_size; + purb->complete = easycap_alsa_complete; + purb->context = peasycap; + purb->start_frame = 0; + purb->number_of_packets = peasycap->audio_isoc_framesperdesc; + for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j].offset = + j * peasycap->audio_isoc_maxframesize; + purb->iso_frame_desc[j].length = + peasycap->audio_isoc_maxframesize; + } + } + JOM(4, "allocation of %i struct urb done.\n", k); + return 0; +} + +static void free_audio_urbs(struct easycap *peasycap) +{ + struct list_head *plist_head, *plist_next; + struct data_urb *pdata_urb; + int m; + + if (peasycap->purb_audio_head) { + JOM(4, "freeing audio urbs\n"); + m = 0; + list_for_each(plist_head, (peasycap->purb_audio_head)) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + usb_free_urb(pdata_urb->purb); + pdata_urb->purb = NULL; + peasycap->allocation_audio_urb--; + m++; + } + } + JOM(4, "%i audio urbs freed\n", m); + JOM(4, "freeing audio data_urb structures.\n"); + m = 0; + list_for_each_safe(plist_head, plist_next, + peasycap->purb_audio_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + peasycap->allocation_audio_struct -= + sizeof(struct data_urb); + kfree(pdata_urb); + m++; + } + } + JOM(4, "%i audio data_urb structures freed\n", m); + JOM(4, "setting peasycap->purb_audio_head=NULL\n"); + peasycap->purb_audio_head = NULL; + } +} + +static void config_easycap(struct easycap *peasycap, + u8 bInterfaceNumber, + u8 bInterfaceClass, + u8 bInterfaceSubClass) +{ + if ((USB_CLASS_VIDEO == bInterfaceClass) || + (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) { + if (-1 == peasycap->video_interface) { + peasycap->video_interface = bInterfaceNumber; + JOM(4, "setting peasycap->video_interface=%i\n", + peasycap->video_interface); + } else { + if (peasycap->video_interface != bInterfaceNumber) { + SAM("ERROR: attempting to reset " + "peasycap->video_interface\n"); + SAM("...... continuing with " + "%i=peasycap->video_interface\n", + peasycap->video_interface); + } + } + } else if ((USB_CLASS_AUDIO == bInterfaceClass) && + (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) { + if (-1 == peasycap->audio_interface) { + peasycap->audio_interface = bInterfaceNumber; + JOM(4, "setting peasycap->audio_interface=%i\n", + peasycap->audio_interface); + } else { + if (peasycap->audio_interface != bInterfaceNumber) { + SAM("ERROR: attempting to reset " + "peasycap->audio_interface\n"); + SAM("...... continuing with " + "%i=peasycap->audio_interface\n", + peasycap->audio_interface); + } + } + } +} + +/* + * This function is called from within easycap_usb_disconnect() and is + * protected by semaphores set and cleared by easycap_usb_disconnect(). + * By this stage the device has already been physically unplugged, + * so peasycap->pusb_device is no longer valid. + */ +static void easycap_delete(struct kref *pkref) +{ + struct easycap *peasycap; + + peasycap = container_of(pkref, struct easycap, kref); + if (!peasycap) { + SAM("ERROR: peasycap is NULL: cannot perform deletions\n"); + return; + } + + /* Free video urbs */ + free_video_urbs(peasycap); + + /* Free video isoc buffers */ + free_isocbuffers(peasycap); + + /* Free video field buffers */ + free_fieldbuffers(peasycap); + + /* Free video frame buffers */ + free_framebuffers(peasycap); + + /* Free audio urbs */ + free_audio_urbs(peasycap); + + /* Free audio isoc buffers */ + free_audio_buffers(peasycap); + + free_easycap(peasycap); + + JOT(4, "ending.\n"); +} + static const struct v4l2_file_operations v4l2_fops = { .owner = THIS_MODULE, .open = easycap_open_noinode, @@ -2850,6 +3450,36 @@ static const struct v4l2_file_operations v4l2_fops = { .mmap = easycap_mmap, }; +static int easycap_register_video(struct easycap *peasycap) +{ + /* + * FIXME: This is believed to be harmless, + * but may well be unnecessary or wrong. + */ + peasycap->video_device.v4l2_dev = NULL; + + strcpy(&peasycap->video_device.name[0], "easycapdc60"); + peasycap->video_device.fops = &v4l2_fops; + peasycap->video_device.minor = -1; + peasycap->video_device.release = (void *)(&videodev_release); + + video_set_drvdata(&(peasycap->video_device), (void *)peasycap); + + if (0 != (video_register_device(&(peasycap->video_device), + VFL_TYPE_GRABBER, -1))) { + videodev_release(&(peasycap->video_device)); + return -ENODEV; + } + + peasycap->registered_video++; + + SAM("registered with videodev: %i=minor\n", + peasycap->video_device.minor); + peasycap->minor = peasycap->video_device.minor; + + return 0; +} + /* * When the device is plugged, this function is called three times, * one for each interface. @@ -2861,24 +3491,15 @@ static int easycap_usb_probe(struct usb_interface *intf, struct usb_host_interface *alt; struct usb_endpoint_descriptor *ep; struct usb_interface_descriptor *interface; - struct urb *purb; struct easycap *peasycap; - int ndong; - struct data_urb *pdata_urb; - int i, j, k, m, rc; + int i, j, rc; u8 bInterfaceNumber; u8 bInterfaceClass; u8 bInterfaceSubClass; - void *pbuf; int okalt[8], isokalt; int okepn[8]; int okmps[8]; int maxpacketsize; - u16 mask; - s32 value; - struct easycap_format *peasycap_format; - int fmtidx; - struct inputset *inputset; usbdev = interface_to_usbdev(intf); @@ -2916,76 +3537,16 @@ static int easycap_usb_probe(struct usb_interface *intf, * interfaces 1 and 2 are probed. */ if (0 == bInterfaceNumber) { - peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL); - if (!peasycap) { - SAY("ERROR: Could not allocate peasycap\n"); + /* + * Alloc structure and save it in a free slot in + * easycapdc60_dongle array + */ + peasycap = alloc_easycap(bInterfaceNumber); + if (!peasycap) return -ENOMEM; - } - /* Perform urgent initializations */ - peasycap->minor = -1; - kref_init(&peasycap->kref); - JOM(8, "intf[%i]: after kref_init(..._video) " - "%i=peasycap->kref.refcount.counter\n", - bInterfaceNumber, peasycap->kref.refcount.counter); - - /* module params */ - peasycap->gain = (s8)clamp(easycap_gain, 0, 31); - - init_waitqueue_head(&peasycap->wq_video); - init_waitqueue_head(&peasycap->wq_audio); - init_waitqueue_head(&peasycap->wq_trigger); - - if (mutex_lock_interruptible(&mutex_dongle)) { - SAY("ERROR: cannot down mutex_dongle\n"); - return -ERESTARTSYS; - } - - for (ndong = 0; ndong < DONGLE_MANY; ndong++) { - if ((!easycapdc60_dongle[ndong].peasycap) && - (!mutex_is_locked(&easycapdc60_dongle - [ndong].mutex_video)) && - (!mutex_is_locked(&easycapdc60_dongle - [ndong].mutex_audio))) { - easycapdc60_dongle[ndong].peasycap = peasycap; - peasycap->isdongle = ndong; - JOM(8, "intf[%i]: peasycap-->easycap" - "_dongle[%i].peasycap\n", - bInterfaceNumber, ndong); - break; - } - } - - if (DONGLE_MANY <= ndong) { - SAM("ERROR: too many dongles\n"); - mutex_unlock(&mutex_dongle); - return -ENOMEM; - } - mutex_unlock(&mutex_dongle); - - peasycap->allocation_video_struct = sizeof(struct easycap); - - /* and further initialize the structure */ - peasycap->pusb_device = usbdev; - peasycap->pusb_interface = intf; - - peasycap->microphone = false; - - peasycap->video_interface = -1; - peasycap->video_altsetting_on = -1; - peasycap->video_altsetting_off = -1; - peasycap->video_endpointnumber = -1; - peasycap->video_isoc_maxframesize = -1; - peasycap->video_isoc_buffer_size = -1; - - peasycap->audio_interface = -1; - peasycap->audio_altsetting_on = -1; - peasycap->audio_altsetting_off = -1; - peasycap->audio_endpointnumber = -1; - peasycap->audio_isoc_maxframesize = -1; - peasycap->audio_isoc_buffer_size = -1; - - peasycap->frame_buffer_many = FRAME_BUFFER_MANY; + /* Perform basic struct initialization */ + init_easycap(peasycap, usbdev, intf, bInterfaceNumber); /* Dynamically fill in the available formats */ rc = easycap_video_fillin_formats(); @@ -2996,136 +3557,19 @@ static int easycap_usb_probe(struct usb_interface *intf, JOM(4, "%i formats available\n", rc); /* Populate easycap.inputset[] */ - inputset = peasycap->inputset; - fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; - m = 0; - mask = 0; - for (i = 0; 0xFFFF != easycap_standard[i].mask; i++) { - if (fmtidx == easycap_standard[i].v4l2_standard.index) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].standard_offset = i; - - mask = easycap_standard[i].mask; - } - } - if (1 != m) { - SAM("ERROR: " - "inputset->standard_offset unpopulated, %i=m\n", m); - return -ENOENT; - } - - peasycap_format = &easycap_format[0]; - m = 0; - for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) { - struct v4l2_pix_format *pix = - &peasycap_format->v4l2_format.fmt.pix; - if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) && - pix->field == V4L2_FIELD_NONE && - pix->pixelformat == V4L2_PIX_FMT_UYVY && - pix->width == 640 && pix->height == 480) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].format_offset = i; - break; - } - peasycap_format++; - } - if (1 != m) { - SAM("ERROR: inputset[]->format_offset unpopulated\n"); - return -ENOENT; - } - - m = 0; - for (i = 0; 0xFFFFFFFF != easycap_control[i].id; i++) { - value = easycap_control[i].default_value; - if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].brightness = value; - } else if (V4L2_CID_CONTRAST == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].contrast = value; - } else if (V4L2_CID_SATURATION == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].saturation = value; - } else if (V4L2_CID_HUE == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].hue = value; - } - } - - if (4 != m) { - SAM("ERROR: inputset[]->brightness underpopulated\n"); - return -ENOENT; - } - for (k = 0; k < INPUT_MANY; k++) - inputset[k].input = k; - JOM(4, "populated inputset[]\n"); + rc = populate_inputset(peasycap); + if (rc < 0) + return rc; JOM(4, "finished initialization\n"); } else { - - /* - * FIXME: Identify the appropriate pointer - * peasycap for interfaces 1 and 2. - * The address of peasycap->pusb_device - * is reluctantly used for this purpose. - */ - for (ndong = 0; ndong < DONGLE_MANY; ndong++) { - if (usbdev == easycapdc60_dongle[ndong].peasycap-> - pusb_device) { - peasycap = easycapdc60_dongle[ndong].peasycap; - JOT(8, "intf[%i]: dongle[%i].peasycap\n", - bInterfaceNumber, ndong); - break; - } - } - if (DONGLE_MANY <= ndong) { - SAY("ERROR: peasycap is unknown when probing interface %i\n", - bInterfaceNumber); + peasycap = get_easycap(usbdev, bInterfaceNumber); + if (!peasycap) return -ENODEV; - } - if (!peasycap) { - SAY("ERROR: peasycap is NULL when probing interface %i\n", - bInterfaceNumber); - return -ENODEV; - } } - if ((USB_CLASS_VIDEO == bInterfaceClass) || - (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) { - if (-1 == peasycap->video_interface) { - peasycap->video_interface = bInterfaceNumber; - JOM(4, "setting peasycap->video_interface=%i\n", - peasycap->video_interface); - } else { - if (peasycap->video_interface != bInterfaceNumber) { - SAM("ERROR: attempting to reset " - "peasycap->video_interface\n"); - SAM("...... continuing with " - "%i=peasycap->video_interface\n", - peasycap->video_interface); - } - } - } else if ((USB_CLASS_AUDIO == bInterfaceClass) && - (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) { - if (-1 == peasycap->audio_interface) { - peasycap->audio_interface = bInterfaceNumber; - JOM(4, "setting peasycap->audio_interface=%i\n", - peasycap->audio_interface); - } else { - if (peasycap->audio_interface != bInterfaceNumber) { - SAM("ERROR: attempting to reset " - "peasycap->audio_interface\n"); - SAM("...... continuing with " - "%i=peasycap->audio_interface\n", - peasycap->audio_interface); - } - } - } + config_easycap(peasycap, bInterfaceNumber, + bInterfaceClass, + bInterfaceSubClass); /* * Investigate all altsettings. This is done in detail @@ -3368,173 +3812,23 @@ static int easycap_usb_probe(struct usb_interface *intf, */ INIT_LIST_HEAD(&(peasycap->urb_video_head)); peasycap->purb_video_head = &(peasycap->urb_video_head); - JOM(4, "allocating %i frame buffers of size %li\n", - FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE); - JOM(4, ".... each scattered over %li pages\n", - FRAME_BUFFER_SIZE/PAGE_SIZE); - for (k = 0; k < FRAME_BUFFER_MANY; k++) { - for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->frame_buffer[k][m].pgo) - SAM("attempting to reallocate frame " - " buffers\n"); - else { - pbuf = (void *)__get_free_page(GFP_KERNEL); - if (!pbuf) { - SAM("ERROR: Could not allocate frame " - "buffer %i page %i\n", k, m); - return -ENOMEM; - } + rc = alloc_framebuffers(peasycap); + if (rc < 0) + return rc; - peasycap->allocation_video_page += 1; - peasycap->frame_buffer[k][m].pgo = pbuf; - } - peasycap->frame_buffer[k][m].pto = - peasycap->frame_buffer[k][m].pgo; - } - } + rc = alloc_fieldbuffers(peasycap); + if (rc < 0) + return rc; - peasycap->frame_fill = 0; - peasycap->frame_read = 0; - JOM(4, "allocation of frame buffers done: %i pages\n", k * - m); - JOM(4, "allocating %i field buffers of size %li\n", - FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE); - JOM(4, ".... each scattered over %li pages\n", - FIELD_BUFFER_SIZE/PAGE_SIZE); + rc = alloc_isocbuffers(peasycap); + if (rc < 0) + return rc; - for (k = 0; k < FIELD_BUFFER_MANY; k++) { - for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->field_buffer[k][m].pgo) { - SAM("ERROR: attempting to reallocate " - "field buffers\n"); - } else { - pbuf = (void *) __get_free_page(GFP_KERNEL); - if (!pbuf) { - SAM("ERROR: Could not allocate field" - " buffer %i page %i\n", k, m); - return -ENOMEM; - } - - peasycap->allocation_video_page += 1; - peasycap->field_buffer[k][m].pgo = pbuf; - } - peasycap->field_buffer[k][m].pto = - peasycap->field_buffer[k][m].pgo; - } - peasycap->field_buffer[k][0].kount = 0x0200; - } - peasycap->field_fill = 0; - peasycap->field_page = 0; - peasycap->field_read = 0; - JOM(4, "allocation of field buffers done: %i pages\n", k * - m); - JOM(4, "allocating %i isoc video buffers of size %i\n", - VIDEO_ISOC_BUFFER_MANY, - peasycap->video_isoc_buffer_size); - JOM(4, ".... each occupying contiguous memory pages\n"); - - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - pbuf = (void *)__get_free_pages(GFP_KERNEL, - VIDEO_ISOC_ORDER); - if (!pbuf) { - SAM("ERROR: Could not allocate isoc video buffer " - "%i\n", k); - return -ENOMEM; - } - peasycap->allocation_video_page += - BIT(VIDEO_ISOC_ORDER); - - peasycap->video_isoc_buffer[k].pgo = pbuf; - peasycap->video_isoc_buffer[k].pto = - pbuf + peasycap->video_isoc_buffer_size; - peasycap->video_isoc_buffer[k].kount = k; - } - JOM(4, "allocation of isoc video buffers done: %i pages\n", - k * (0x01 << VIDEO_ISOC_ORDER)); - - /* Allocate and initialize multiple struct usb */ - JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY); - JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n", - peasycap->video_isoc_framesperdesc); - JOM(4, "using %i=peasycap->video_isoc_maxframesize\n", - peasycap->video_isoc_maxframesize); - JOM(4, "using %i=peasycap->video_isoc_buffer_sizen", - peasycap->video_isoc_buffer_size); - - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc, - GFP_KERNEL); - if (!purb) { - SAM("ERROR: usb_alloc_urb returned NULL for buffer " - "%i\n", k); - return -ENOMEM; - } - - peasycap->allocation_video_urb += 1; - pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); - if (!pdata_urb) { - SAM("ERROR: Could not allocate struct data_urb.\n"); - return -ENOMEM; - } - - peasycap->allocation_video_struct += - sizeof(struct data_urb); - - pdata_urb->purb = purb; - pdata_urb->isbuf = k; - pdata_urb->length = 0; - list_add_tail(&(pdata_urb->list_head), - peasycap->purb_video_head); - - /* Initialize allocated urbs */ - if (!k) { - JOM(4, "initializing video urbs thus:\n"); - JOM(4, " purb->interval = 1;\n"); - JOM(4, " purb->dev = peasycap->pusb_device;\n"); - JOM(4, " purb->pipe = usb_rcvisocpipe" - "(peasycap->pusb_device,%i);\n", - peasycap->video_endpointnumber); - JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); - JOM(4, " purb->transfer_buffer = peasycap->" - "video_isoc_buffer[.].pgo;\n"); - JOM(4, " purb->transfer_buffer_length = %i;\n", - peasycap->video_isoc_buffer_size); - JOM(4, " purb->complete = easycap_complete;\n"); - JOM(4, " purb->context = peasycap;\n"); - JOM(4, " purb->start_frame = 0;\n"); - JOM(4, " purb->number_of_packets = %i;\n", - peasycap->video_isoc_framesperdesc); - JOM(4, " for (j = 0; j < %i; j++)\n", - peasycap->video_isoc_framesperdesc); - JOM(4, " {\n"); - JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", - peasycap->video_isoc_maxframesize); - JOM(4, " purb->iso_frame_desc[j].length = %i;\n", - peasycap->video_isoc_maxframesize); - JOM(4, " }\n"); - } - - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, - peasycap->video_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = peasycap->video_isoc_buffer[k].pgo; - purb->transfer_buffer_length = - peasycap->video_isoc_buffer_size; - purb->complete = easycap_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = peasycap->video_isoc_framesperdesc; - for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { - purb->iso_frame_desc[j].offset = j * - peasycap->video_isoc_maxframesize; - purb->iso_frame_desc[j].length = - peasycap->video_isoc_maxframesize; - } - } - JOM(4, "allocation of %i struct urb done.\n", k); + /* Allocate and initialize video urbs */ + rc = create_video_urbs(peasycap); + if (rc < 0) + return rc; /* Save pointer peasycap in this interface */ usb_set_intfdata(intf, peasycap); @@ -3545,9 +3839,6 @@ static int easycap_usb_probe(struct usb_interface *intf, * because some udev rules triggers easycap_open() * immediately after registration, causing a clash. */ - peasycap->ntsc = easycap_ntsc; - JOM(8, "defaulting initially to %s\n", - easycap_ntsc ? "NTSC" : "PAL"); rc = reset(peasycap); if (rc) { SAM("ERROR: reset() rc = %i\n", rc); @@ -3562,33 +3853,12 @@ static int easycap_usb_probe(struct usb_interface *intf, JOM(4, "registered device instance: %s\n", peasycap->v4l2_device.name); - /* - * FIXME: This is believed to be harmless, - * but may well be unnecessary or wrong. - */ - peasycap->video_device.v4l2_dev = NULL; - - - strcpy(&peasycap->video_device.name[0], "easycapdc60"); - peasycap->video_device.fops = &v4l2_fops; - peasycap->video_device.minor = -1; - peasycap->video_device.release = (void *)(&videodev_release); - - video_set_drvdata(&(peasycap->video_device), (void *)peasycap); - - if (0 != (video_register_device(&(peasycap->video_device), - VFL_TYPE_GRABBER, -1))) { + rc = easycap_register_video(peasycap); + if (rc < 0) { dev_err(&intf->dev, "Not able to register with videodev\n"); - videodev_release(&(peasycap->video_device)); return -ENODEV; } - - peasycap->registered_video++; - SAM("registered with videodev: %i=minor\n", - peasycap->video_device.minor); - peasycap->minor = peasycap->video_device.minor; - break; } /* 1: Audio control */ @@ -3711,109 +3981,14 @@ static int easycap_usb_probe(struct usb_interface *intf, INIT_LIST_HEAD(&(peasycap->urb_audio_head)); peasycap->purb_audio_head = &(peasycap->urb_audio_head); - JOM(4, "allocating %i isoc audio buffers of size %i\n", - AUDIO_ISOC_BUFFER_MANY, - peasycap->audio_isoc_buffer_size); - JOM(4, ".... each occupying contiguous memory pages\n"); - - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - pbuf = (void *)__get_free_pages(GFP_KERNEL, - AUDIO_ISOC_ORDER); - if (!pbuf) { - SAM("ERROR: Could not allocate isoc audio buffer " - "%i\n", k); - return -ENOMEM; - } - peasycap->allocation_audio_page += - BIT(AUDIO_ISOC_ORDER); - - peasycap->audio_isoc_buffer[k].pgo = pbuf; - peasycap->audio_isoc_buffer[k].pto = pbuf + - peasycap->audio_isoc_buffer_size; - peasycap->audio_isoc_buffer[k].kount = k; - } - JOM(4, "allocation of isoc audio buffers done.\n"); + alloc_audio_buffers(peasycap); + if (rc < 0) + return rc; /* Allocate and initialize urbs */ - JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY); - JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n", - peasycap->audio_isoc_maxframesize); - JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n", - peasycap->audio_isoc_buffer_size); - - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc, - GFP_KERNEL); - if (!purb) { - SAM("ERROR: usb_alloc_urb returned NULL for buffer " - "%i\n", k); - return -ENOMEM; - } - peasycap->allocation_audio_urb += 1 ; - pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); - if (!pdata_urb) { - usb_free_urb(purb); - SAM("ERROR: Could not allocate struct data_urb.\n"); - return -ENOMEM; - } - peasycap->allocation_audio_struct += - sizeof(struct data_urb); - - pdata_urb->purb = purb; - pdata_urb->isbuf = k; - pdata_urb->length = 0; - list_add_tail(&(pdata_urb->list_head), - peasycap->purb_audio_head); - - if (!k) { - JOM(4, "initializing audio urbs thus:\n"); - JOM(4, " purb->interval = 1;\n"); - JOM(4, " purb->dev = peasycap->pusb_device;\n"); - JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->" - "pusb_device,%i);\n", - peasycap->audio_endpointnumber); - JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); - JOM(4, " purb->transfer_buffer = " - "peasycap->audio_isoc_buffer[.].pgo;\n"); - JOM(4, " purb->transfer_buffer_length = %i;\n", - peasycap->audio_isoc_buffer_size); - JOM(4, " purb->complete = easycap_alsa_complete;\n"); - JOM(4, " purb->context = peasycap;\n"); - JOM(4, " purb->start_frame = 0;\n"); - JOM(4, " purb->number_of_packets = %i;\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, " for (j = 0; j < %i; j++)\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, " {\n"); - JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", - peasycap->audio_isoc_maxframesize); - JOM(4, " purb->iso_frame_desc[j].length = %i;\n", - peasycap->audio_isoc_maxframesize); - JOM(4, " }\n"); - } - - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, - peasycap->audio_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo; - purb->transfer_buffer_length = - peasycap->audio_isoc_buffer_size; - purb->complete = easycap_alsa_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = peasycap->audio_isoc_framesperdesc; - for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { - purb->iso_frame_desc[j].offset = j * - peasycap->audio_isoc_maxframesize; - purb->iso_frame_desc[j].length = - peasycap->audio_isoc_maxframesize; - } - } - JOM(4, "allocation of %i struct urb done.\n", k); + rc = create_audio_urbs(peasycap); + if (rc < 0) + return rc; /* Save pointer peasycap in this interface */ usb_set_intfdata(intf, peasycap); @@ -3843,15 +4018,13 @@ static int easycap_usb_probe(struct usb_interface *intf, SAM("ends successfully for interface %i\n", bInterfaceNumber); return 0; } -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ + /* - * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY - * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID. - * - * THIS FUNCTION AFFECTS ALSA. BEWARE. + * When this function is called the device has already been + * physically unplugged. + * Hence, peasycap->pusb_device is no longer valid. + * This function affects alsa. */ -/*---------------------------------------------------------------------------*/ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) { struct usb_host_interface *pusb_host_interface; @@ -3876,6 +4049,7 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) minor = pusb_interface->minor; JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor); + /* There is nothing to do for Interface Number 1 */ if (1 == bInterfaceNumber) return; @@ -3884,11 +4058,8 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) SAY("ERROR: peasycap is NULL\n"); return; } -/*---------------------------------------------------------------------------*/ -/* - * IF THE WAIT QUEUES ARE NOT CLEARED A DEADLOCK IS POSSIBLE. BEWARE. -*/ -/*---------------------------------------------------------------------------*/ + + /* If the waitqueues are not cleared a deadlock is possible */ peasycap->video_eof = 1; peasycap->audio_eof = 1; wake_up_interruptible(&(peasycap->wq_video)); @@ -3904,15 +4075,14 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) default: break; } -/*--------------------------------------------------------------------------*/ -/* - * DEREGISTER - * - * THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO - * IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN - * AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE. - */ -/*--------------------------------------------------------------------------*/ + + /* + * Deregister + * This procedure will block until easycap_poll(), + * video and audio ioctl are all unlocked. + * If this is not done an oops can occur when an easycap + * is unplugged while the urbs are running. + */ kd = easycap_isdongle(peasycap); switch (bInterfaceNumber) { case 0: { @@ -3929,7 +4099,6 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) } else { SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd); } -/*---------------------------------------------------------------------------*/ if (!peasycap->v4l2_device.name[0]) { SAM("ERROR: peasycap->v4l2_device.name is empty\n"); if (0 <= kd && DONGLE_MANY > kd) @@ -3945,7 +4114,6 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) JOM(4, "intf[%i]: video_unregister_device() minor=%i\n", bInterfaceNumber, minor); peasycap->registered_video--; -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ if (0 <= kd && DONGLE_MANY > kd) { mutex_unlock(&easycapdc60_dongle[kd].mutex_video); @@ -3981,12 +4149,12 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) default: break; } -/*---------------------------------------------------------------------------*/ -/* - * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap - * (ALSO WHEN ALSA HAS BEEN IN USE) - */ -/*---------------------------------------------------------------------------*/ + + /* + * If no remaining references to peasycap, + * call easycap_delete. + * (Also when alsa has been in use) + */ if (!peasycap->kref.refcount.counter) { SAM("ERROR: peasycap->kref.refcount.counter is zero " "so cannot call kref_put()\n"); @@ -4021,17 +4189,11 @@ static void easycap_usb_disconnect(struct usb_interface *pusb_interface) mutex_unlock(&easycapdc60_dongle[kd].mutex_video); JOT(4, "unlocked dongle[%i].mutex_video\n", kd); } -/*---------------------------------------------------------------------------*/ JOM(4, "ends\n"); return; } -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO - */ -/*---------------------------------------------------------------------------*/ +/* Devices supported by this driver */ static struct usb_device_id easycap_usb_device_id_table[] = { {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)}, { } @@ -4066,14 +4228,11 @@ static int __init easycap_module_init(void) return rc; } -/*****************************************************************************/ + static void __exit easycap_module_exit(void) { usb_deregister(&easycap_usb_driver); } -/*****************************************************************************/ module_init(easycap_module_init); module_exit(easycap_module_exit); - -/*****************************************************************************/ diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c index 3ef4cd8b4de3..c184ad30fbd8 100644 --- a/drivers/staging/media/go7007/go7007-v4l2.c +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -76,7 +76,6 @@ static void abort_queued(struct go7007 *go) static int go7007_streamoff(struct go7007 *go) { - int retval = -EINVAL; unsigned long flags; mutex_lock(&go->hw_lock); @@ -87,7 +86,6 @@ static int go7007_streamoff(struct go7007 *go) abort_queued(go); spin_unlock_irqrestore(&go->spinlock, flags); go7007_reset_encoder(go); - retval = 0; } mutex_unlock(&go->hw_lock); return 0; diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c index 7c5af4f289b6..f1bd159ac195 100644 --- a/drivers/staging/media/go7007/s2250-loader.c +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -165,3 +165,5 @@ module_usb_driver(s2250loader_driver); MODULE_AUTHOR(""); MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251"); MODULE_LICENSE("GPL v2"); +MODULE_FIRMWARE(S2250_LOADER_FIRMWARE); +MODULE_FIRMWARE(S2250_FIRMWARE); diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c index d7cf5ef076a5..2944fde89f44 100644 --- a/drivers/staging/media/lirc/lirc_imon.c +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -70,10 +70,6 @@ static ssize_t vfd_write(struct file *file, const char __user *buf, static int ir_open(void *data); static void ir_close(void *data); -/* Driver init/exit prototypes */ -static int __init imon_init(void); -static void __exit imon_exit(void); - /*** G L O B A L S ***/ #define IMON_DATA_BUF_SZ 35 diff --git a/drivers/staging/media/lirc/lirc_sasem.c b/drivers/staging/media/lirc/lirc_sasem.c index 352a20229ca2..f4e4d9003f38 100644 --- a/drivers/staging/media/lirc/lirc_sasem.c +++ b/drivers/staging/media/lirc/lirc_sasem.c @@ -80,10 +80,6 @@ static ssize_t vfd_write(struct file *file, const char *buf, static int ir_open(void *data); static void ir_close(void *data); -/* Driver init/exit prototypes */ -static int __init sasem_init(void); -static void __exit sasem_exit(void); - /*** G L O B A L S ***/ #define SASEM_DATA_BUF_SZ 32 diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/uvc_queue.c index 0cdf89d32a15..104ae9c81251 100644 --- a/drivers/usb/gadget/uvc_queue.c +++ b/drivers/usb/gadget/uvc_queue.c @@ -543,7 +543,7 @@ done: return ret; } -/* called with queue->irqlock held.. */ +/* called with &queue_irqlock held.. */ static struct uvc_buffer * uvc_queue_next_buffer(struct uvc_video_queue *queue, struct uvc_buffer *buf) { diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index 54d7ca559cb2..2ca9386d655b 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c @@ -296,7 +296,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) return -EINVAL; - return v4l2_event_subscribe(&handle->vfh, arg, 2); + return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL); } case VIDIOC_UNSUBSCRIBE_EVENT: diff --git a/include/linux/Kbuild b/include/linux/Kbuild index 39737839ce29..74af192ef7ae 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -383,6 +383,7 @@ header-y += utime.h header-y += utsname.h header-y += uuid.h header-y += uvcvideo.h +header-y += v4l2-dv-timings.h header-y += v4l2-mediabus.h header-y += v4l2-subdev.h header-y += veth.h diff --git a/include/linux/dvb/frontend.h b/include/linux/dvb/frontend.h index cb4428ab81ed..f50d4058c5fb 100644 --- a/include/linux/dvb/frontend.h +++ b/include/linux/dvb/frontend.h @@ -320,7 +320,24 @@ struct dvb_frontend_event { #define DTV_ENUM_DELSYS 44 -#define DTV_MAX_COMMAND DTV_ENUM_DELSYS +/* ATSC-MH */ +#define DTV_ATSCMH_FIC_VER 45 +#define DTV_ATSCMH_PARADE_ID 46 +#define DTV_ATSCMH_NOG 47 +#define DTV_ATSCMH_TNOG 48 +#define DTV_ATSCMH_SGN 49 +#define DTV_ATSCMH_PRC 50 +#define DTV_ATSCMH_RS_FRAME_MODE 51 +#define DTV_ATSCMH_RS_FRAME_ENSEMBLE 52 +#define DTV_ATSCMH_RS_CODE_MODE_PRI 53 +#define DTV_ATSCMH_RS_CODE_MODE_SEC 54 +#define DTV_ATSCMH_SCCC_BLOCK_MODE 55 +#define DTV_ATSCMH_SCCC_CODE_MODE_A 56 +#define DTV_ATSCMH_SCCC_CODE_MODE_B 57 +#define DTV_ATSCMH_SCCC_CODE_MODE_C 58 +#define DTV_ATSCMH_SCCC_CODE_MODE_D 59 + +#define DTV_MAX_COMMAND DTV_ATSCMH_SCCC_CODE_MODE_D typedef enum fe_pilot { PILOT_ON, @@ -360,6 +377,38 @@ typedef enum fe_delivery_system { #define SYS_DVBC_ANNEX_AC SYS_DVBC_ANNEX_A +/* ATSC-MH */ + +enum atscmh_sccc_block_mode { + ATSCMH_SCCC_BLK_SEP = 0, + ATSCMH_SCCC_BLK_COMB = 1, + ATSCMH_SCCC_BLK_RES = 2, +}; + +enum atscmh_sccc_code_mode { + ATSCMH_SCCC_CODE_HLF = 0, + ATSCMH_SCCC_CODE_QTR = 1, + ATSCMH_SCCC_CODE_RES = 2, +}; + +enum atscmh_rs_frame_ensemble { + ATSCMH_RSFRAME_ENS_PRI = 0, + ATSCMH_RSFRAME_ENS_SEC = 1, +}; + +enum atscmh_rs_frame_mode { + ATSCMH_RSFRAME_PRI_ONLY = 0, + ATSCMH_RSFRAME_PRI_SEC = 1, + ATSCMH_RSFRAME_RES = 2, +}; + +enum atscmh_rs_code_mode { + ATSCMH_RSCODE_211_187 = 0, + ATSCMH_RSCODE_223_187 = 1, + ATSCMH_RSCODE_235_187 = 2, + ATSCMH_RSCODE_RES = 3, +}; + struct dtv_cmds_h { char *name; /* A display name for debugging purposes */ diff --git a/include/linux/dvb/version.h b/include/linux/dvb/version.h index 0559e2bd38f9..43d9e8d462d4 100644 --- a/include/linux/dvb/version.h +++ b/include/linux/dvb/version.h @@ -24,6 +24,6 @@ #define _DVBVERSION_H_ #define DVB_API_VERSION 5 -#define DVB_API_VERSION_MINOR 5 +#define DVB_API_VERSION_MINOR 6 #endif /*_DVBVERSION_H_*/ diff --git a/drivers/input/fixp-arith.h b/include/linux/fixp-arith.h similarity index 100% rename from drivers/input/fixp-arith.h rename to include/linux/fixp-arith.h diff --git a/include/linux/v4l2-dv-timings.h b/include/linux/v4l2-dv-timings.h new file mode 100644 index 000000000000..9ef8172e5ed0 --- /dev/null +++ b/include/linux/v4l2-dv-timings.h @@ -0,0 +1,816 @@ +/* + * V4L2 DV timings header. + * + * Copyright (C) 2012 Hans Verkuil + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + */ + +#ifndef _V4L2_DV_TIMINGS_H +#define _V4L2_DV_TIMINGS_H + +#if __GNUC__ < 4 || (__GNUC__ == 4 && (__GNUC_MINOR__ < 6)) +/* Sadly gcc versions older than 4.6 have a bug in how they initialize + anonymous unions where they require additional curly brackets. + This violates the C1x standard. This workaround adds the curly brackets + if needed. */ +#define V4L2_INIT_BT_TIMINGS(_width, args...) \ + { .bt = { _width , ## args } } +#else +#define V4L2_INIT_BT_TIMINGS(_width, args...) \ + .bt = { _width , ## args } +#endif + +/* CEA-861-E timings (i.e. standard HDTV timings) */ + +#define V4L2_DV_BT_CEA_640X480P59_94 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 480, 0, 0, \ + 25175000, 16, 96, 48, 10, 2, 33, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_720X480P59_94 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(720, 480, 0, 0, \ + 27000000, 16, 62, 60, 9, 6, 30, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_720X576P50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(720, 576, 0, 0, \ + 27000000, 12, 64, 68, 5, 5, 39, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1280X720P24 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 59400000, 1760, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1280X720P25 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 2420, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1280X720P30 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 1760, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1280X720P50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 440, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1280X720P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 720, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 110, 40, 220, 5, 5, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P24 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 638, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P25 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 528, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P30 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 88, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + +#define V4L2_DV_BT_CEA_1920X1080I50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 528, 44, 148, 2, 5, 15, 2, 5, 16, \ + V4L2_DV_BT_STD_CEA861, V4L2_DV_FL_HALF_LINE) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P50 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 148500000, 528, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_CEA861, 0) \ +} + +#define V4L2_DV_BT_CEA_1920X1080I60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 1, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 74250000, 88, 44, 148, 2, 5, 15, 2, 5, 16, \ + V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS | V4L2_DV_FL_HALF_LINE) \ +} + +#define V4L2_DV_BT_CEA_1920X1080P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1080, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 148500000, 88, 44, 148, 4, 5, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CEA861, \ + V4L2_DV_FL_CAN_REDUCE_FPS) \ +} + + +/* VESA Discrete Monitor Timings as per version 1.0, revision 12 */ + +#define V4L2_DV_BT_DMT_640X350P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 350, 0, V4L2_DV_HSYNC_POS_POL, \ + 31500000, 32, 64, 96, 32, 3, 60, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_640X400P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 400, 0, V4L2_DV_VSYNC_POS_POL, \ + 31500000, 32, 64, 96, 1, 3, 41, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_720X400P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(720, 400, 0, V4L2_DV_VSYNC_POS_POL, \ + 35500000, 36, 72, 108, 1, 3, 42, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +/* VGA resolutions */ +#define V4L2_DV_BT_DMT_640X480P60 V4L2_DV_BT_CEA_640X480P59_94 + +#define V4L2_DV_BT_DMT_640X480P72 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 480, 0, 0, \ + 31500000, 24, 40, 128, 9, 3, 28, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_640X480P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 480, 0, 0, \ + 31500000, 16, 64, 120, 1, 3, 16, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_640X480P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(640, 480, 0, 0, \ + 36000000, 56, 56, 80, 1, 3, 25, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +/* SVGA resolutions */ +#define V4L2_DV_BT_DMT_800X600P56 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 36000000, 24, 72, 128, 1, 2, 22, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 40000000, 40, 128, 88, 1, 4, 23, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P72 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 50000000, 56, 120, 64, 37, 6, 23, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 49500000, 16, 80, 160, 1, 3, 21, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 56250000, 32, 64, 152, 1, 3, 27, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_800X600P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(800, 600, 0, V4L2_DV_HSYNC_POS_POL, \ + 73250000, 48, 32, 80, 3, 4, 29, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_848X480P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(848, 480, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 33750000, 16, 112, 112, 6, 8, 23, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768I43 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 1, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 44900000, 8, 176, 56, 0, 4, 20, 0, 4, 21, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +/* XGA resolutions */ +#define V4L2_DV_BT_DMT_1024X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, 0, \ + 65000000, 24, 136, 160, 3, 6, 29, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768P70 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, 0, \ + 75000000, 24, 136, 144, 3, 6, 29, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 78750000, 16, 96, 176, 1, 3, 28, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 94500000, 48, 96, 208, 1, 3, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1024X768P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1024, 768, 0, V4L2_DV_HSYNC_POS_POL, \ + 115500000, 48, 32, 80, 3, 4, 38, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* XGA+ resolution */ +#define V4L2_DV_BT_DMT_1152X864P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1152, 864, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 108000000, 64, 128, 256, 1, 3, 32, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X720P60 V4L2_DV_BT_CEA_1280X720P60 + +/* WXGA resolutions */ +#define V4L2_DV_BT_DMT_1280X768P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_HSYNC_POS_POL, \ + 68250000, 48, 32, 80, 3, 7, 12, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1280X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_VSYNC_POS_POL, \ + 79500000, 64, 128, 192, 3, 7, 20, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X768P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_VSYNC_POS_POL, \ + 102250000, 80, 128, 208, 3, 7, 27, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X768P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_VSYNC_POS_POL, \ + 117500000, 80, 136, 216, 3, 7, 31, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X768P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 768, 0, V4L2_DV_HSYNC_POS_POL, \ + 140250000, 48, 32, 80, 3, 7, 35, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1280X800P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_HSYNC_POS_POL, \ + 71000000, 48, 32, 80, 3, 6, 14, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1280X800P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_VSYNC_POS_POL, \ + 83500000, 72, 128, 200, 3, 6, 22, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X800P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_VSYNC_POS_POL, \ + 106500000, 80, 128, 208, 3, 6, 29, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X800P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_VSYNC_POS_POL, \ + 122500000, 80, 136, 216, 3, 6, 34, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X800P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 800, 0, V4L2_DV_HSYNC_POS_POL, \ + 146250000, 48, 32, 80, 3, 6, 38, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1280X960P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 960, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 108000000, 96, 112, 312, 1, 3, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X960P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 960, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 148500000, 64, 160, 224, 1, 3, 47, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X960P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 960, 0, V4L2_DV_HSYNC_POS_POL, \ + 175500000, 48, 32, 80, 3, 4, 50, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* SXGA resolutions */ +#define V4L2_DV_BT_DMT_1280X1024P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 1024, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 108000000, 48, 112, 248, 1, 3, 38, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X1024P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 1024, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 135000000, 16, 144, 248, 1, 3, 38, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X1024P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 1024, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 157500000, 64, 160, 224, 1, 3, 44, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1280X1024P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1280, 1024, 0, V4L2_DV_HSYNC_POS_POL, \ + 187250000, 48, 32, 80, 3, 7, 50, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1360X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1360, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 85500000, 64, 112, 256, 3, 6, 18, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1360X768P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1360, 768, 0, V4L2_DV_HSYNC_POS_POL, \ + 148250000, 48, 32, 80, 3, 5, 37, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1366X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1366, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 85500000, 70, 143, 213, 3, 3, 24, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1366X768P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1366, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 72000000, 14, 56, 64, 1, 3, 28, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* SXGA+ resolutions */ +#define V4L2_DV_BT_DMT_1400X1050P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_HSYNC_POS_POL, \ + 101000000, 48, 32, 80, 3, 4, 23, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1400X1050P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 121750000, 88, 144, 232, 3, 4, 32, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1400X1050P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 156000000, 104, 144, 248, 3, 4, 42, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1400X1050P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 179500000, 104, 152, 256, 3, 4, 48, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1400X1050P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1400, 1050, 0, V4L2_DV_HSYNC_POS_POL, \ + 208000000, 48, 32, 80, 3, 4, 55, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* WXGA+ resolutions */ +#define V4L2_DV_BT_DMT_1440X900P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_HSYNC_POS_POL, \ + 88750000, 48, 32, 80, 3, 6, 17, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1440X900P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_VSYNC_POS_POL, \ + 106500000, 80, 152, 232, 3, 6, 25, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1440X900P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_VSYNC_POS_POL, \ + 136750000, 96, 152, 248, 3, 6, 33, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1440X900P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_VSYNC_POS_POL, \ + 157000000, 104, 152, 256, 3, 6, 39, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1440X900P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1440, 900, 0, V4L2_DV_HSYNC_POS_POL, \ + 182750000, 48, 32, 80, 3, 6, 44, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1600X900P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 900, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 108000000, 24, 80, 96, 1, 3, 96, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* UXGA resolutions */ +#define V4L2_DV_BT_DMT_1600X1200P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 162000000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P65 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 175500000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P70 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 189000000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 202500000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 229500000, 64, 192, 304, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1600X1200P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1600, 1200, 0, V4L2_DV_HSYNC_POS_POL, \ + 268250000, 48, 32, 80, 3, 4, 64, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* WSXGA+ resolutions */ +#define V4L2_DV_BT_DMT_1680X1050P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_HSYNC_POS_POL, \ + 119000000, 48, 32, 80, 3, 6, 21, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1680X1050P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 146250000, 104, 176, 280, 3, 6, 30, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1680X1050P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 187000000, 120, 176, 296, 3, 6, 40, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1680X1050P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_VSYNC_POS_POL, \ + 214750000, 128, 176, 304, 3, 6, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1680X1050P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1680, 1050, 0, V4L2_DV_HSYNC_POS_POL, \ + 245500000, 48, 32, 80, 3, 6, 53, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1792X1344P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1792, 1344, 0, V4L2_DV_VSYNC_POS_POL, \ + 204750000, 128, 200, 328, 1, 3, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1792X1344P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1792, 1344, 0, V4L2_DV_VSYNC_POS_POL, \ + 261000000, 96, 216, 352, 1, 3, 69, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1792X1344P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1792, 1344, 0, V4L2_DV_HSYNC_POS_POL, \ + 333250000, 48, 32, 80, 3, 4, 72, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1856X1392P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1856, 1392, 0, V4L2_DV_VSYNC_POS_POL, \ + 218250000, 96, 224, 352, 1, 3, 43, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1856X1392P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1856, 1392, 0, V4L2_DV_VSYNC_POS_POL, \ + 288000000, 128, 224, 352, 1, 3, 104, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1856X1392P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1856, 1392, 0, V4L2_DV_HSYNC_POS_POL, \ + 356500000, 48, 32, 80, 3, 4, 75, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1920X1080P60 V4L2_DV_BT_CEA_1920X1080P60 + +/* WUXGA resolutions */ +#define V4L2_DV_BT_DMT_1920X1200P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_HSYNC_POS_POL, \ + 154000000, 48, 32, 80, 3, 6, 26, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1920X1200P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_VSYNC_POS_POL, \ + 193250000, 136, 200, 336, 3, 6, 36, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1200P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_VSYNC_POS_POL, \ + 245250000, 136, 208, 344, 3, 6, 46, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1200P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_VSYNC_POS_POL, \ + 281250000, 144, 208, 352, 3, 6, 53, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1200P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1200, 0, V4L2_DV_HSYNC_POS_POL, \ + 317000000, 48, 32, 80, 3, 6, 62, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1920X1440P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1440, 0, V4L2_DV_VSYNC_POS_POL, \ + 234000000, 128, 208, 344, 1, 3, 56, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1440P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1440, 0, V4L2_DV_VSYNC_POS_POL, \ + 297000000, 144, 224, 352, 1, 3, 56, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#define V4L2_DV_BT_DMT_1920X1440P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1920, 1440, 0, V4L2_DV_HSYNC_POS_POL, \ + 380500000, 48, 32, 80, 3, 4, 78, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_2048X1152P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2048, 1152, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 162000000, 26, 80, 96, 1, 3, 44, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, V4L2_DV_FL_REDUCED_BLANKING) \ +} + +/* WQXGA resolutions */ +#define V4L2_DV_BT_DMT_2560X1600P60_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_HSYNC_POS_POL, \ + 268500000, 48, 32, 80, 3, 6, 37, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_2560X1600P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_VSYNC_POS_POL, \ + 348500000, 192, 280, 472, 3, 6, 49, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_2560X1600P75 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_VSYNC_POS_POL, \ + 443250000, 208, 280, 488, 3, 6, 63, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_2560X1600P85 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_VSYNC_POS_POL, \ + 505250000, 208, 280, 488, 3, 6, 73, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, 0) \ +} + +#define V4L2_DV_BT_DMT_2560X1600P120_RB { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(2560, 1600, 0, V4L2_DV_HSYNC_POS_POL, \ + 552750000, 48, 32, 80, 3, 6, 85, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT | V4L2_DV_BT_STD_CVT, \ + V4L2_DV_FL_REDUCED_BLANKING) \ +} + +#define V4L2_DV_BT_DMT_1366X768P60 { \ + .type = V4L2_DV_BT_656_1120, \ + V4L2_INIT_BT_TIMINGS(1366, 768, 0, \ + V4L2_DV_HSYNC_POS_POL | V4L2_DV_VSYNC_POS_POL, \ + 85500000, 70, 143, 213, 3, 3, 24, 0, 0, 0, \ + V4L2_DV_BT_STD_DMT, 0) \ +} + +#endif diff --git a/include/linux/v4l2-subdev.h b/include/linux/v4l2-subdev.h index ed29cbbebfef..812019ee1e06 100644 --- a/include/linux/v4l2-subdev.h +++ b/include/linux/v4l2-subdev.h @@ -123,6 +123,43 @@ struct v4l2_subdev_frame_interval_enum { __u32 reserved[9]; }; +#define V4L2_SUBDEV_SEL_FLAG_SIZE_GE (1 << 0) +#define V4L2_SUBDEV_SEL_FLAG_SIZE_LE (1 << 1) +#define V4L2_SUBDEV_SEL_FLAG_KEEP_CONFIG (1 << 2) + +/* active cropping area */ +#define V4L2_SUBDEV_SEL_TGT_CROP_ACTUAL 0x0000 +/* cropping bounds */ +#define V4L2_SUBDEV_SEL_TGT_CROP_BOUNDS 0x0002 +/* current composing area */ +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_ACTUAL 0x0100 +/* composing bounds */ +#define V4L2_SUBDEV_SEL_TGT_COMPOSE_BOUNDS 0x0102 + + +/** + * struct v4l2_subdev_selection - selection info + * + * @which: either V4L2_SUBDEV_FORMAT_ACTIVE or V4L2_SUBDEV_FORMAT_TRY + * @pad: pad number, as reported by the media API + * @target: selection target, used to choose one of possible rectangles + * @flags: constraint flags + * @r: coordinates of the selection window + * @reserved: for future use, set to zero for now + * + * Hardware may use multiple helper windows to process a video stream. + * The structure is used to exchange this selection areas between + * an application and a driver. + */ +struct v4l2_subdev_selection { + __u32 which; + __u32 pad; + __u32 target; + __u32 flags; + struct v4l2_rect r; + __u32 reserved[8]; +}; + #define VIDIOC_SUBDEV_G_FMT _IOWR('V', 4, struct v4l2_subdev_format) #define VIDIOC_SUBDEV_S_FMT _IOWR('V', 5, struct v4l2_subdev_format) #define VIDIOC_SUBDEV_G_FRAME_INTERVAL \ @@ -137,5 +174,9 @@ struct v4l2_subdev_frame_interval_enum { _IOWR('V', 75, struct v4l2_subdev_frame_interval_enum) #define VIDIOC_SUBDEV_G_CROP _IOWR('V', 59, struct v4l2_subdev_crop) #define VIDIOC_SUBDEV_S_CROP _IOWR('V', 60, struct v4l2_subdev_crop) +#define VIDIOC_SUBDEV_G_SELECTION \ + _IOWR('V', 61, struct v4l2_subdev_selection) +#define VIDIOC_SUBDEV_S_SELECTION \ + _IOWR('V', 62, struct v4l2_subdev_selection) #endif diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h index c9c9a4680cc5..370d11106c11 100644 --- a/include/linux/videodev2.h +++ b/include/linux/videodev2.h @@ -292,10 +292,10 @@ struct v4l2_pix_format { __u32 width; __u32 height; __u32 pixelformat; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ __u32 bytesperline; /* for padding, zero if unused */ __u32 sizeimage; - enum v4l2_colorspace colorspace; + __u32 colorspace; /* enum v4l2_colorspace */ __u32 priv; /* private data, depends on pixelformat */ }; @@ -378,7 +378,10 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_SGRBG12 v4l2_fourcc('B', 'A', '1', '2') /* 12 GRGR.. BGBG.. */ #define V4L2_PIX_FMT_SRGGB12 v4l2_fourcc('R', 'G', '1', '2') /* 12 RGRG.. GBGB.. */ /* 10bit raw bayer DPCM compressed to 8 bits */ +#define V4L2_PIX_FMT_SBGGR10DPCM8 v4l2_fourcc('b', 'B', 'A', '8') +#define V4L2_PIX_FMT_SGBRG10DPCM8 v4l2_fourcc('b', 'G', 'A', '8') #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0') +#define V4L2_PIX_FMT_SRGGB10DPCM8 v4l2_fourcc('b', 'R', 'A', '8') /* * 10bit raw bayer, expanded to 16 bits * xxxxrrrrrrrrrrxxxxgggggggggg xxxxggggggggggxxxxbbbbbbbbbb... @@ -432,7 +435,7 @@ struct v4l2_pix_format { */ struct v4l2_fmtdesc { __u32 index; /* Format number */ - enum v4l2_buf_type type; /* buffer type */ + __u32 type; /* enum v4l2_buf_type */ __u32 flags; __u8 description[32]; /* Description string */ __u32 pixelformat; /* Format fourcc */ @@ -573,8 +576,8 @@ struct v4l2_jpegcompression { */ struct v4l2_requestbuffers { __u32 count; - enum v4l2_buf_type type; - enum v4l2_memory memory; + __u32 type; /* enum v4l2_buf_type */ + __u32 memory; /* enum v4l2_memory */ __u32 reserved[2]; }; @@ -610,15 +613,17 @@ struct v4l2_plane { /** * struct v4l2_buffer - video buffer info * @index: id number of the buffer - * @type: buffer type (type == *_MPLANE for multiplanar buffers) + * @type: enum v4l2_buf_type; buffer type (type == *_MPLANE for + * multiplanar buffers); * @bytesused: number of bytes occupied by data in the buffer (payload); * unused (set to 0) for multiplanar buffers * @flags: buffer informational flags - * @field: field order of the image in the buffer + * @field: enum v4l2_field; field order of the image in the buffer * @timestamp: frame timestamp * @timecode: frame timecode * @sequence: sequence count of this frame - * @memory: the method, in which the actual video data is passed + * @memory: enum v4l2_memory; the method, in which the actual video data is + * passed * @offset: for non-multiplanar buffers with memory == V4L2_MEMORY_MMAP; * offset from the start of the device memory for this plane, * (or a "cookie" that should be passed to mmap() as offset) @@ -636,16 +641,16 @@ struct v4l2_plane { */ struct v4l2_buffer { __u32 index; - enum v4l2_buf_type type; + __u32 type; __u32 bytesused; __u32 flags; - enum v4l2_field field; + __u32 field; struct timeval timestamp; struct v4l2_timecode timecode; __u32 sequence; /* memory location */ - enum v4l2_memory memory; + __u32 memory; union { __u32 offset; unsigned long userptr; @@ -708,7 +713,7 @@ struct v4l2_clip { struct v4l2_window { struct v4l2_rect w; - enum v4l2_field field; + __u32 field; /* enum v4l2_field */ __u32 chromakey; struct v4l2_clip __user *clips; __u32 clipcount; @@ -745,14 +750,14 @@ struct v4l2_outputparm { * I N P U T I M A G E C R O P P I N G */ struct v4l2_cropcap { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ struct v4l2_rect bounds; struct v4l2_rect defrect; struct v4l2_fract pixelaspect; }; struct v4l2_crop { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ struct v4l2_rect c; }; @@ -939,6 +944,9 @@ struct v4l2_standard { __u32 reserved[4]; }; +/* The DV Preset API is deprecated in favor of the DV Timings API. + New drivers shouldn't use this anymore! */ + /* * V I D E O T I M I N G S D V P R E S E T */ @@ -986,29 +994,56 @@ struct v4l2_dv_enum_preset { * D V B T T I M I N G S */ -/* BT.656/BT.1120 timing data */ +/** struct v4l2_bt_timings - BT.656/BT.1120 timing data + * @width: total width of the active video in pixels + * @height: total height of the active video in lines + * @interlaced: Interlaced or progressive + * @polarities: Positive or negative polarities + * @pixelclock: Pixel clock in HZ. Ex. 74.25MHz->74250000 + * @hfrontporch:Horizontal front porch in pixels + * @hsync: Horizontal Sync length in pixels + * @hbackporch: Horizontal back porch in pixels + * @vfrontporch:Vertical front porch in lines + * @vsync: Vertical Sync length in lines + * @vbackporch: Vertical back porch in lines + * @il_vfrontporch:Vertical front porch for the even field + * (aka field 2) of interlaced field formats + * @il_vsync: Vertical Sync length for the even field + * (aka field 2) of interlaced field formats + * @il_vbackporch:Vertical back porch for the even field + * (aka field 2) of interlaced field formats + * @standards: Standards the timing belongs to + * @flags: Flags + * @reserved: Reserved fields, must be zeroed. + * + * A note regarding vertical interlaced timings: height refers to the total + * height of the active video frame (= two fields). The blanking timings refer + * to the blanking of each field. So the height of the total frame is + * calculated as follows: + * + * tot_height = height + vfrontporch + vsync + vbackporch + + * il_vfrontporch + il_vsync + il_vbackporch + * + * The active height of each field is height / 2. + */ struct v4l2_bt_timings { - __u32 width; /* width in pixels */ - __u32 height; /* height in lines */ - __u32 interlaced; /* Interlaced or progressive */ - __u32 polarities; /* Positive or negative polarity */ - __u64 pixelclock; /* Pixel clock in HZ. Ex. 74.25MHz->74250000 */ - __u32 hfrontporch; /* Horizpontal front porch in pixels */ - __u32 hsync; /* Horizontal Sync length in pixels */ - __u32 hbackporch; /* Horizontal back porch in pixels */ - __u32 vfrontporch; /* Vertical front porch in pixels */ - __u32 vsync; /* Vertical Sync length in lines */ - __u32 vbackporch; /* Vertical back porch in lines */ - __u32 il_vfrontporch; /* Vertical front porch for bottom field of - * interlaced field formats - */ - __u32 il_vsync; /* Vertical sync length for bottom field of - * interlaced field formats - */ - __u32 il_vbackporch; /* Vertical back porch for bottom field of - * interlaced field formats - */ - __u32 reserved[16]; + __u32 width; + __u32 height; + __u32 interlaced; + __u32 polarities; + __u64 pixelclock; + __u32 hfrontporch; + __u32 hsync; + __u32 hbackporch; + __u32 vfrontporch; + __u32 vsync; + __u32 vbackporch; + __u32 il_vfrontporch; + __u32 il_vsync; + __u32 il_vbackporch; + __u32 standards; + __u32 flags; + __u32 reserved[14]; } __attribute__ ((packed)); /* Interlaced or progressive format */ @@ -1019,8 +1054,42 @@ struct v4l2_bt_timings { #define V4L2_DV_VSYNC_POS_POL 0x00000001 #define V4L2_DV_HSYNC_POS_POL 0x00000002 +/* Timings standards */ +#define V4L2_DV_BT_STD_CEA861 (1 << 0) /* CEA-861 Digital TV Profile */ +#define V4L2_DV_BT_STD_DMT (1 << 1) /* VESA Discrete Monitor Timings */ +#define V4L2_DV_BT_STD_CVT (1 << 2) /* VESA Coordinated Video Timings */ +#define V4L2_DV_BT_STD_GTF (1 << 3) /* VESA Generalized Timings Formula */ -/* DV timings */ +/* Flags */ + +/* CVT/GTF specific: timing uses reduced blanking (CVT) or the 'Secondary + GTF' curve (GTF). In both cases the horizontal and/or vertical blanking + intervals are reduced, allowing a higher resolution over the same + bandwidth. This is a read-only flag. */ +#define V4L2_DV_FL_REDUCED_BLANKING (1 << 0) +/* CEA-861 specific: set for CEA-861 formats with a framerate of a multiple + of six. These formats can be optionally played at 1 / 1.001 speed. + This is a read-only flag. */ +#define V4L2_DV_FL_CAN_REDUCE_FPS (1 << 1) +/* CEA-861 specific: only valid for video transmitters, the flag is cleared + by receivers. + If the framerate of the format is a multiple of six, then the pixelclock + used to set up the transmitter is divided by 1.001 to make it compatible + with 60 Hz based standards such as NTSC and PAL-M that use a framerate of + 29.97 Hz. Otherwise this flag is cleared. If the transmitter can't generate + such frequencies, then the flag will also be cleared. */ +#define V4L2_DV_FL_REDUCED_FPS (1 << 2) +/* Specific to interlaced formats: if set, then field 1 is really one half-line + longer and field 2 is really one half-line shorter, so each field has + exactly the same number of half-lines. Whether half-lines can be detected + or used depends on the hardware. */ +#define V4L2_DV_FL_HALF_LINE (1 << 0) + + +/** struct v4l2_dv_timings - DV timings + * @type: the type of the timings + * @bt: BT656/1120 timings + */ struct v4l2_dv_timings { __u32 type; union { @@ -1032,6 +1101,64 @@ struct v4l2_dv_timings { /* Values for the type field */ #define V4L2_DV_BT_656_1120 0 /* BT.656/1120 timing type */ + +/** struct v4l2_enum_dv_timings - DV timings enumeration + * @index: enumeration index + * @reserved: must be zeroed + * @timings: the timings for the given index + */ +struct v4l2_enum_dv_timings { + __u32 index; + __u32 reserved[3]; + struct v4l2_dv_timings timings; +}; + +/** struct v4l2_bt_timings_cap - BT.656/BT.1120 timing capabilities + * @min_width: width in pixels + * @max_width: width in pixels + * @min_height: height in lines + * @max_height: height in lines + * @min_pixelclock: Pixel clock in HZ. Ex. 74.25MHz->74250000 + * @max_pixelclock: Pixel clock in HZ. Ex. 74.25MHz->74250000 + * @standards: Supported standards + * @capabilities: Supported capabilities + * @reserved: Must be zeroed + */ +struct v4l2_bt_timings_cap { + __u32 min_width; + __u32 max_width; + __u32 min_height; + __u32 max_height; + __u64 min_pixelclock; + __u64 max_pixelclock; + __u32 standards; + __u32 capabilities; + __u32 reserved[16]; +} __attribute__ ((packed)); + +/* Supports interlaced formats */ +#define V4L2_DV_BT_CAP_INTERLACED (1 << 0) +/* Supports progressive formats */ +#define V4L2_DV_BT_CAP_PROGRESSIVE (1 << 1) +/* Supports CVT/GTF reduced blanking */ +#define V4L2_DV_BT_CAP_REDUCED_BLANKING (1 << 2) +/* Supports custom formats */ +#define V4L2_DV_BT_CAP_CUSTOM (1 << 3) + +/** struct v4l2_dv_timings_cap - DV timings capabilities + * @type: the type of the timings (same as in struct v4l2_dv_timings) + * @bt: the BT656/1120 timings capabilities + */ +struct v4l2_dv_timings_cap { + __u32 type; + __u32 reserved[3]; + union { + struct v4l2_bt_timings_cap bt; + __u32 raw_data[32]; + }; +}; + + /* * V I D E O I N P U T S */ @@ -1040,7 +1167,7 @@ struct v4l2_input { __u8 name[32]; /* Label */ __u32 type; /* Type of input */ __u32 audioset; /* Associated audios (bitfield) */ - __u32 tuner; /* Associated tuner */ + __u32 tuner; /* enum v4l2_tuner_type */ v4l2_std_id std; __u32 status; __u32 capabilities; @@ -1137,6 +1264,8 @@ struct v4l2_ext_controls { #define V4L2_CTRL_CLASS_FM_TX 0x009b0000 /* FM Modulator control class */ #define V4L2_CTRL_CLASS_FLASH 0x009c0000 /* Camera flash controls */ #define V4L2_CTRL_CLASS_JPEG 0x009d0000 /* JPEG-compression controls */ +#define V4L2_CTRL_CLASS_IMAGE_SOURCE 0x009e0000 /* Image source controls */ +#define V4L2_CTRL_CLASS_IMAGE_PROC 0x009f0000 /* Image processing controls */ #define V4L2_CTRL_ID_MASK (0x0fffffff) #define V4L2_CTRL_ID2CLASS(id) ((id) & 0x0fff0000UL) @@ -1151,12 +1280,13 @@ enum v4l2_ctrl_type { V4L2_CTRL_TYPE_CTRL_CLASS = 6, V4L2_CTRL_TYPE_STRING = 7, V4L2_CTRL_TYPE_BITMASK = 8, + V4L2_CTRL_TYPE_INTEGER_MENU = 9, }; /* Used in the VIDIOC_QUERYCTRL ioctl for querying controls */ struct v4l2_queryctrl { __u32 id; - enum v4l2_ctrl_type type; + __u32 type; /* enum v4l2_ctrl_type */ __u8 name[32]; /* Whatever */ __s32 minimum; /* Note signedness */ __s32 maximum; @@ -1170,9 +1300,12 @@ struct v4l2_queryctrl { struct v4l2_querymenu { __u32 id; __u32 index; - __u8 name[32]; /* Whatever */ + union { + __u8 name[32]; /* Whatever */ + __s64 value; + }; __u32 reserved; -}; +} __attribute__ ((packed)); /* Control flags */ #define V4L2_CTRL_FLAG_DISABLED 0x0001 @@ -1237,16 +1370,22 @@ enum v4l2_power_line_frequency { #define V4L2_CID_COLOR_KILLER (V4L2_CID_BASE+30) #define V4L2_CID_COLORFX (V4L2_CID_BASE+31) enum v4l2_colorfx { - V4L2_COLORFX_NONE = 0, - V4L2_COLORFX_BW = 1, - V4L2_COLORFX_SEPIA = 2, - V4L2_COLORFX_NEGATIVE = 3, - V4L2_COLORFX_EMBOSS = 4, - V4L2_COLORFX_SKETCH = 5, - V4L2_COLORFX_SKY_BLUE = 6, - V4L2_COLORFX_GRASS_GREEN = 7, - V4L2_COLORFX_SKIN_WHITEN = 8, - V4L2_COLORFX_VIVID = 9, + V4L2_COLORFX_NONE = 0, + V4L2_COLORFX_BW = 1, + V4L2_COLORFX_SEPIA = 2, + V4L2_COLORFX_NEGATIVE = 3, + V4L2_COLORFX_EMBOSS = 4, + V4L2_COLORFX_SKETCH = 5, + V4L2_COLORFX_SKY_BLUE = 6, + V4L2_COLORFX_GRASS_GREEN = 7, + V4L2_COLORFX_SKIN_WHITEN = 8, + V4L2_COLORFX_VIVID = 9, + V4L2_COLORFX_AQUA = 10, + V4L2_COLORFX_ART_FREEZE = 11, + V4L2_COLORFX_SILHOUETTE = 12, + V4L2_COLORFX_SOLARIZATION = 13, + V4L2_COLORFX_ANTIQUE = 14, + V4L2_COLORFX_SET_CBCR = 15, }; #define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32) #define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33) @@ -1263,9 +1402,10 @@ enum v4l2_colorfx { #define V4L2_CID_MIN_BUFFERS_FOR_OUTPUT (V4L2_CID_BASE+40) #define V4L2_CID_ALPHA_COMPONENT (V4L2_CID_BASE+41) +#define V4L2_CID_COLORFX_CBCR (V4L2_CID_BASE+42) /* last CID + 1 */ -#define V4L2_CID_LASTP1 (V4L2_CID_BASE+42) +#define V4L2_CID_LASTP1 (V4L2_CID_BASE+43) /* MPEG-class control IDs defined by V4L2 */ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900) @@ -1689,6 +1829,78 @@ enum v4l2_exposure_auto_type { #define V4L2_CID_IRIS_ABSOLUTE (V4L2_CID_CAMERA_CLASS_BASE+17) #define V4L2_CID_IRIS_RELATIVE (V4L2_CID_CAMERA_CLASS_BASE+18) +#define V4L2_CID_AUTO_EXPOSURE_BIAS (V4L2_CID_CAMERA_CLASS_BASE+19) + +#define V4L2_CID_AUTO_N_PRESET_WHITE_BALANCE (V4L2_CID_CAMERA_CLASS_BASE+20) +enum v4l2_auto_n_preset_white_balance { + V4L2_WHITE_BALANCE_MANUAL = 0, + V4L2_WHITE_BALANCE_AUTO = 1, + V4L2_WHITE_BALANCE_INCANDESCENT = 2, + V4L2_WHITE_BALANCE_FLUORESCENT = 3, + V4L2_WHITE_BALANCE_FLUORESCENT_H = 4, + V4L2_WHITE_BALANCE_HORIZON = 5, + V4L2_WHITE_BALANCE_DAYLIGHT = 6, + V4L2_WHITE_BALANCE_FLASH = 7, + V4L2_WHITE_BALANCE_CLOUDY = 8, + V4L2_WHITE_BALANCE_SHADE = 9, +}; + +#define V4L2_CID_WIDE_DYNAMIC_RANGE (V4L2_CID_CAMERA_CLASS_BASE+21) +#define V4L2_CID_IMAGE_STABILIZATION (V4L2_CID_CAMERA_CLASS_BASE+22) + +#define V4L2_CID_ISO_SENSITIVITY (V4L2_CID_CAMERA_CLASS_BASE+23) +#define V4L2_CID_ISO_SENSITIVITY_AUTO (V4L2_CID_CAMERA_CLASS_BASE+24) +enum v4l2_iso_sensitivity_auto_type { + V4L2_ISO_SENSITIVITY_MANUAL = 0, + V4L2_ISO_SENSITIVITY_AUTO = 1, +}; + +#define V4L2_CID_EXPOSURE_METERING (V4L2_CID_CAMERA_CLASS_BASE+25) +enum v4l2_exposure_metering { + V4L2_EXPOSURE_METERING_AVERAGE = 0, + V4L2_EXPOSURE_METERING_CENTER_WEIGHTED = 1, + V4L2_EXPOSURE_METERING_SPOT = 2, +}; + +#define V4L2_CID_SCENE_MODE (V4L2_CID_CAMERA_CLASS_BASE+26) +enum v4l2_scene_mode { + V4L2_SCENE_MODE_NONE = 0, + V4L2_SCENE_MODE_BACKLIGHT = 1, + V4L2_SCENE_MODE_BEACH_SNOW = 2, + V4L2_SCENE_MODE_CANDLE_LIGHT = 3, + V4L2_SCENE_MODE_DAWN_DUSK = 4, + V4L2_SCENE_MODE_FALL_COLORS = 5, + V4L2_SCENE_MODE_FIREWORKS = 6, + V4L2_SCENE_MODE_LANDSCAPE = 7, + V4L2_SCENE_MODE_NIGHT = 8, + V4L2_SCENE_MODE_PARTY_INDOOR = 9, + V4L2_SCENE_MODE_PORTRAIT = 10, + V4L2_SCENE_MODE_SPORTS = 11, + V4L2_SCENE_MODE_SUNSET = 12, + V4L2_SCENE_MODE_TEXT = 13, +}; + +#define V4L2_CID_3A_LOCK (V4L2_CID_CAMERA_CLASS_BASE+27) +#define V4L2_LOCK_EXPOSURE (1 << 0) +#define V4L2_LOCK_WHITE_BALANCE (1 << 1) +#define V4L2_LOCK_FOCUS (1 << 2) + +#define V4L2_CID_AUTO_FOCUS_START (V4L2_CID_CAMERA_CLASS_BASE+28) +#define V4L2_CID_AUTO_FOCUS_STOP (V4L2_CID_CAMERA_CLASS_BASE+29) +#define V4L2_CID_AUTO_FOCUS_STATUS (V4L2_CID_CAMERA_CLASS_BASE+30) +#define V4L2_AUTO_FOCUS_STATUS_IDLE (0 << 0) +#define V4L2_AUTO_FOCUS_STATUS_BUSY (1 << 0) +#define V4L2_AUTO_FOCUS_STATUS_REACHED (1 << 1) +#define V4L2_AUTO_FOCUS_STATUS_FAILED (1 << 2) + +#define V4L2_CID_AUTO_FOCUS_RANGE (V4L2_CID_CAMERA_CLASS_BASE+31) +enum v4l2_auto_focus_range { + V4L2_AUTO_FOCUS_RANGE_AUTO = 0, + V4L2_AUTO_FOCUS_RANGE_NORMAL = 1, + V4L2_AUTO_FOCUS_RANGE_MACRO = 2, + V4L2_AUTO_FOCUS_RANGE_INFINITY = 3, +}; + /* FM Modulator class control IDs */ #define V4L2_CID_FM_TX_CLASS_BASE (V4L2_CTRL_CLASS_FM_TX | 0x900) #define V4L2_CID_FM_TX_CLASS (V4L2_CTRL_CLASS_FM_TX | 1) @@ -1782,13 +1994,28 @@ enum v4l2_jpeg_chroma_subsampling { #define V4L2_JPEG_ACTIVE_MARKER_DQT (1 << 17) #define V4L2_JPEG_ACTIVE_MARKER_DHT (1 << 18) +/* Image source controls */ +#define V4L2_CID_IMAGE_SOURCE_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_SOURCE | 0x900) +#define V4L2_CID_IMAGE_SOURCE_CLASS (V4L2_CTRL_CLASS_IMAGE_SOURCE | 1) + +#define V4L2_CID_VBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 1) +#define V4L2_CID_HBLANK (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 2) +#define V4L2_CID_ANALOGUE_GAIN (V4L2_CID_IMAGE_SOURCE_CLASS_BASE + 3) + +/* Image processing controls */ +#define V4L2_CID_IMAGE_PROC_CLASS_BASE (V4L2_CTRL_CLASS_IMAGE_PROC | 0x900) +#define V4L2_CID_IMAGE_PROC_CLASS (V4L2_CTRL_CLASS_IMAGE_PROC | 1) + +#define V4L2_CID_LINK_FREQ (V4L2_CID_IMAGE_PROC_CLASS_BASE + 1) +#define V4L2_CID_PIXEL_RATE (V4L2_CID_IMAGE_PROC_CLASS_BASE + 2) + /* * T U N I N G */ struct v4l2_tuner { __u32 index; __u8 name[32]; - enum v4l2_tuner_type type; + __u32 type; /* enum v4l2_tuner_type */ __u32 capability; __u32 rangelow; __u32 rangehigh; @@ -1838,14 +2065,14 @@ struct v4l2_modulator { struct v4l2_frequency { __u32 tuner; - enum v4l2_tuner_type type; + __u32 type; /* enum v4l2_tuner_type */ __u32 frequency; __u32 reserved[8]; }; struct v4l2_hw_freq_seek { __u32 tuner; - enum v4l2_tuner_type type; + __u32 type; /* enum v4l2_tuner_type */ __u32 seek_upward; __u32 wrap_around; __u32 spacing; @@ -2056,7 +2283,7 @@ struct v4l2_sliced_vbi_cap { (equals frame lines 313-336 for 625 line video standards, 263-286 for 525 line standards) */ __u16 service_lines[2][24]; - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ __u32 reserved[3]; /* must be 0 */ }; @@ -2137,8 +2364,8 @@ struct v4l2_plane_pix_format { * @width: image width in pixels * @height: image height in pixels * @pixelformat: little endian four character code (fourcc) - * @field: field order (for interlaced video) - * @colorspace: supplemental to pixelformat + * @field: enum v4l2_field; field order (for interlaced video) + * @colorspace: enum v4l2_colorspace; supplemental to pixelformat * @plane_fmt: per-plane information * @num_planes: number of planes for this format */ @@ -2146,8 +2373,8 @@ struct v4l2_pix_format_mplane { __u32 width; __u32 height; __u32 pixelformat; - enum v4l2_field field; - enum v4l2_colorspace colorspace; + __u32 field; + __u32 colorspace; struct v4l2_plane_pix_format plane_fmt[VIDEO_MAX_PLANES]; __u8 num_planes; @@ -2156,7 +2383,7 @@ struct v4l2_pix_format_mplane { /** * struct v4l2_format - stream data format - * @type: type of the data stream + * @type: enum v4l2_buf_type; type of the data stream * @pix: definition of an image format * @pix_mp: definition of a multiplanar image format * @win: definition of an overlaid image @@ -2165,7 +2392,7 @@ struct v4l2_pix_format_mplane { * @raw_data: placeholder for future extensions and custom formats */ struct v4l2_format { - enum v4l2_buf_type type; + __u32 type; union { struct v4l2_pix_format pix; /* V4L2_BUF_TYPE_VIDEO_CAPTURE */ struct v4l2_pix_format_mplane pix_mp; /* V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE */ @@ -2179,7 +2406,7 @@ struct v4l2_format { /* Stream type-dependent parameters */ struct v4l2_streamparm { - enum v4l2_buf_type type; + __u32 type; /* enum v4l2_buf_type */ union { struct v4l2_captureparm capture; struct v4l2_outputparm output; @@ -2292,14 +2519,14 @@ struct v4l2_dbg_chip_ident { * @index: on return, index of the first created buffer * @count: entry: number of requested buffers, * return: number of created buffers - * @memory: buffer memory type + * @memory: enum v4l2_memory; buffer memory type * @format: frame format, for which buffers are requested * @reserved: future extensions */ struct v4l2_create_buffers { __u32 index; __u32 count; - enum v4l2_memory memory; + __u32 memory; struct v4l2_format format; __u32 reserved[8]; }; @@ -2356,8 +2583,8 @@ struct v4l2_create_buffers { #define VIDIOC_TRY_FMT _IOWR('V', 64, struct v4l2_format) #define VIDIOC_ENUMAUDIO _IOWR('V', 65, struct v4l2_audio) #define VIDIOC_ENUMAUDOUT _IOWR('V', 66, struct v4l2_audioout) -#define VIDIOC_G_PRIORITY _IOR('V', 67, enum v4l2_priority) -#define VIDIOC_S_PRIORITY _IOW('V', 68, enum v4l2_priority) +#define VIDIOC_G_PRIORITY _IOR('V', 67, __u32) /* enum v4l2_priority */ +#define VIDIOC_S_PRIORITY _IOW('V', 68, __u32) /* enum v4l2_priority */ #define VIDIOC_G_SLICED_VBI_CAP _IOWR('V', 69, struct v4l2_sliced_vbi_cap) #define VIDIOC_LOG_STATUS _IO('V', 70) #define VIDIOC_G_EXT_CTRLS _IOWR('V', 71, struct v4l2_ext_controls) @@ -2384,6 +2611,9 @@ struct v4l2_create_buffers { #endif #define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek) + +/* These four DV Preset ioctls are deprecated in favor of the DV Timings + ioctls. */ #define VIDIOC_ENUM_DV_PRESETS _IOWR('V', 83, struct v4l2_dv_enum_preset) #define VIDIOC_S_DV_PRESET _IOWR('V', 84, struct v4l2_dv_preset) #define VIDIOC_G_DV_PRESET _IOWR('V', 85, struct v4l2_dv_preset) @@ -2408,6 +2638,12 @@ struct v4l2_create_buffers { #define VIDIOC_DECODER_CMD _IOWR('V', 96, struct v4l2_decoder_cmd) #define VIDIOC_TRY_DECODER_CMD _IOWR('V', 97, struct v4l2_decoder_cmd) +/* Experimental, these three ioctls may change over the next couple of kernel + versions. */ +#define VIDIOC_ENUM_DV_TIMINGS _IOWR('V', 96, struct v4l2_enum_dv_timings) +#define VIDIOC_QUERY_DV_TIMINGS _IOR('V', 97, struct v4l2_dv_timings) +#define VIDIOC_DV_TIMINGS_CAP _IOWR('V', 98, struct v4l2_dv_timings_cap) + /* Reminder: when adding new ioctls please add support for them to drivers/media/video/v4l2-compat-ioctl32.c as well! */ diff --git a/include/media/media-entity.h b/include/media/media-entity.h index 29e7bba78ffe..0c16f518ee09 100644 --- a/include/media/media-entity.h +++ b/include/media/media-entity.h @@ -46,6 +46,7 @@ struct media_entity_operations { int (*link_setup)(struct media_entity *entity, const struct media_pad *local, const struct media_pad *remote, u32 flags); + int (*link_validate)(struct media_link *link); }; struct media_entity { @@ -140,8 +141,8 @@ void media_entity_graph_walk_start(struct media_entity_graph *graph, struct media_entity *entity); struct media_entity * media_entity_graph_walk_next(struct media_entity_graph *graph); -void media_entity_pipeline_start(struct media_entity *entity, - struct media_pipeline *pipe); +__must_check int media_entity_pipeline_start(struct media_entity *entity, + struct media_pipeline *pipe); void media_entity_pipeline_stop(struct media_entity *entity); #define media_entity_call(entity, operation, args...) \ diff --git a/include/media/mt9p031.h b/include/media/mt9p031.h index 96448c7a318b..0c97b19af293 100644 --- a/include/media/mt9p031.h +++ b/include/media/mt9p031.h @@ -3,17 +3,18 @@ struct v4l2_subdev; -enum { - MT9P031_COLOR_VERSION, - MT9P031_MONOCHROME_VERSION, -}; - +/* + * struct mt9p031_platform_data - MT9P031 platform data + * @set_xclk: Clock frequency set callback + * @reset: Chip reset GPIO (set to -1 if not used) + * @ext_freq: Input clock frequency + * @target_freq: Pixel clock frequency + */ struct mt9p031_platform_data { int (*set_xclk)(struct v4l2_subdev *subdev, int hz); - int (*reset)(struct v4l2_subdev *subdev, int active); - int ext_freq; /* input frequency to the mt9p031 for PLL dividers */ - int target_freq; /* frequency target for the PLL */ - int version; /* MT9P031_COLOR_VERSION or MT9P031_MONOCHROME_VERSION */ + int reset; + int ext_freq; + int target_freq; }; #endif diff --git a/include/media/omap3isp.h b/include/media/omap3isp.h index 042849a34640..4d94be5226af 100644 --- a/include/media/omap3isp.h +++ b/include/media/omap3isp.h @@ -29,6 +29,10 @@ struct i2c_board_info; struct isp_device; +#define ISP_XCLK_NONE 0 +#define ISP_XCLK_A 1 +#define ISP_XCLK_B 2 + enum isp_interface_type { ISP_INTERFACE_PARALLEL, ISP_INTERFACE_CSI2A_PHY2, @@ -86,6 +90,29 @@ enum { ISP_CCP2_MODE_CCP2 = 1, }; +/** + * struct isp_csiphy_lane: CCP2/CSI2 lane position and polarity + * @pos: position of the lane + * @pol: polarity of the lane + */ +struct isp_csiphy_lane { + u8 pos; + u8 pol; +}; + +#define ISP_CSIPHY1_NUM_DATA_LANES 1 +#define ISP_CSIPHY2_NUM_DATA_LANES 2 + +/** + * struct isp_csiphy_lanes_cfg - CCP2/CSI2 lane configuration + * @data: Configuration of one or two data lanes + * @clk: Clock lane configuration + */ +struct isp_csiphy_lanes_cfg { + struct isp_csiphy_lane data[ISP_CSIPHY2_NUM_DATA_LANES]; + struct isp_csiphy_lane clk; +}; + /** * struct isp_ccp2_platform_data - CCP2 interface platform data * @strobe_clk_pol: Strobe/clock polarity @@ -105,6 +132,7 @@ struct isp_ccp2_platform_data { unsigned int ccp2_mode:1; unsigned int phy_layer:1; unsigned int vpclk_div:2; + struct isp_csiphy_lanes_cfg lanecfg; }; /** @@ -115,6 +143,7 @@ struct isp_ccp2_platform_data { struct isp_csi2_platform_data { unsigned crc:1; unsigned vpclk_div:2; + struct isp_csiphy_lanes_cfg lanecfg; }; struct isp_subdev_i2c_board_info { diff --git a/include/media/rc-map.h b/include/media/rc-map.h index 8db6741c1256..cfd5163ff7f3 100644 --- a/include/media/rc-map.h +++ b/include/media/rc-map.h @@ -62,6 +62,7 @@ void rc_map_init(void); #define RC_MAP_ANYSEE "rc-anysee" #define RC_MAP_APAC_VIEWCOMP "rc-apac-viewcomp" #define RC_MAP_ASUS_PC39 "rc-asus-pc39" +#define RC_MAP_ASUS_PS3_100 "rc-asus-ps3-100" #define RC_MAP_ATI_TV_WONDER_HD_600 "rc-ati-tv-wonder-hd-600" #define RC_MAP_ATI_X10 "rc-ati-x10" #define RC_MAP_AVERMEDIA_A16D "rc-avermedia-a16d" @@ -113,6 +114,8 @@ void rc_map_init(void); #define RC_MAP_LME2510 "rc-lme2510" #define RC_MAP_MANLI "rc-manli" #define RC_MAP_MEDION_X10 "rc-medion-x10" +#define RC_MAP_MEDION_X10_DIGITAINER "rc-medion-x10-digitainer" +#define RC_MAP_MEDION_X10_OR2X "rc-medion-x10-or2x" #define RC_MAP_MSI_DIGIVOX_II "rc-msi-digivox-ii" #define RC_MAP_MSI_DIGIVOX_III "rc-msi-digivox-iii" #define RC_MAP_MSI_TVANYWHERE_PLUS "rc-msi-tvanywhere-plus" diff --git a/include/media/s5p_fimc.h b/include/media/s5p_fimc.h index 688fb3f1dc35..8587aaf73646 100644 --- a/include/media/s5p_fimc.h +++ b/include/media/s5p_fimc.h @@ -64,4 +64,20 @@ struct s5p_platform_fimc { */ #define S5P_FIMC_TX_END_NOTIFY _IO('e', 0) +enum fimc_subdev_index { + IDX_SENSOR, + IDX_CSIS, + IDX_FLITE, + IDX_FIMC, + IDX_MAX, +}; + +struct media_pipeline; +struct v4l2_subdev; + +struct fimc_pipeline { + struct v4l2_subdev *subdevs[IDX_MAX]; + struct media_pipeline *m_pipeline; +}; + #endif /* S5P_FIMC_H_ */ diff --git a/include/media/saa7146.h b/include/media/saa7146.h index 0f037e8edf9a..773e527deabe 100644 --- a/include/media/saa7146.h +++ b/include/media/saa7146.h @@ -13,12 +13,11 @@ #include #include #include +#include #include /* for vmalloc() */ #include /* for vmalloc_to_page() */ -#define SAA7146_VERSION_CODE 0x000600 /* 0.6.0 */ - #define saa7146_write(sxy,adr,dat) writel((dat),(sxy->mem+(adr))) #define saa7146_read(sxy,adr) readl(sxy->mem+(adr)) @@ -121,6 +120,7 @@ struct saa7146_dev struct list_head item; struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; /* different device locks */ spinlock_t slock; diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h index 4aeff96ff7d8..944ecdf3530f 100644 --- a/include/media/saa7146_vv.h +++ b/include/media/saa7146_vv.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -84,21 +85,15 @@ struct saa7146_overlay { /* per open data */ struct saa7146_fh { + /* Must be the first field! */ + struct v4l2_fh fh; struct saa7146_dev *dev; - /* if this is a vbi or capture open */ - enum v4l2_buf_type type; - - /* video overlay */ - struct saa7146_overlay ov; /* video capture */ struct videobuf_queue video_q; - struct v4l2_pix_format video_fmt; /* vbi capture */ struct videobuf_queue vbi_q; - struct v4l2_vbi_format vbi_fmt; - struct timer_list vbi_read_timeout; unsigned int resources; /* resource management for device open */ }; @@ -109,7 +104,9 @@ struct saa7146_fh { struct saa7146_vv { /* vbi capture */ - struct saa7146_dmaqueue vbi_q; + struct saa7146_dmaqueue vbi_dmaq; + struct v4l2_vbi_format vbi_fmt; + struct timer_list vbi_read_timeout; /* vbi workaround interrupt queue */ wait_queue_head_t vbi_wq; int vbi_fieldcount; @@ -119,13 +116,14 @@ struct saa7146_vv struct saa7146_fh *video_fh; /* video overlay */ + struct saa7146_overlay ov; struct v4l2_framebuffer ov_fb; struct saa7146_format *ov_fmt; - struct saa7146_overlay *ov_data; struct saa7146_fh *ov_suspend; /* video capture */ - struct saa7146_dmaqueue video_q; + struct saa7146_dmaqueue video_dmaq; + struct v4l2_pix_format video_fmt; enum v4l2_field last_field; /* common: fixme? shouldn't this be in saa7146_fh? @@ -163,7 +161,8 @@ struct saa7146_ext_vv int (*std_callback)(struct saa7146_dev*, struct saa7146_standard *); /* the extension can override this */ - struct v4l2_ioctl_ops ops; + struct v4l2_ioctl_ops vid_ops; + struct v4l2_ioctl_ops vbi_ops; /* pointer to the saa7146 core ops */ const struct v4l2_ioctl_ops *core_ops; @@ -202,10 +201,12 @@ void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data); /* from saa7146_video.c */ extern const struct v4l2_ioctl_ops saa7146_video_ioctl_ops; +extern const struct v4l2_ioctl_ops saa7146_vbi_ioctl_ops; extern struct saa7146_use_ops saa7146_video_uops; int saa7146_start_preview(struct saa7146_fh *fh); int saa7146_stop_preview(struct saa7146_fh *fh); long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg); +int saa7146_s_ctrl(struct v4l2_ctrl *ctrl); /* from saa7146_vbi.c */ extern struct saa7146_use_ops saa7146_vbi_uops; diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h index a90a765f18da..6fdb6adf6b2b 100644 --- a/include/media/sh_mobile_ceu.h +++ b/include/media/sh_mobile_ceu.h @@ -5,6 +5,7 @@ #define SH_CEU_FLAG_USE_16BIT_BUS (1 << 1) /* use 16bit bus width */ #define SH_CEU_FLAG_HSYNC_LOW (1 << 2) /* default High if possible */ #define SH_CEU_FLAG_VSYNC_LOW (1 << 3) /* default High if possible */ +#define SH_CEU_FLAG_LOWER_8BIT (1 << 4) /* default upper 8bit */ struct device; struct resource; diff --git a/include/media/smiapp.h b/include/media/smiapp.h new file mode 100644 index 000000000000..9ab07fd45d5c --- /dev/null +++ b/include/media/smiapp.h @@ -0,0 +1,84 @@ +/* + * include/media/smiapp.h + * + * Generic driver for SMIA/SMIA++ compliant camera modules + * + * Copyright (C) 2011--2012 Nokia Corporation + * Contact: Sakari Ailus + * + * 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., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA + * + */ + +#ifndef __SMIAPP_H_ +#define __SMIAPP_H_ + +#include + +#define SMIAPP_NAME "smiapp" + +#define SMIAPP_DFL_I2C_ADDR (0x20 >> 1) /* Default I2C Address */ +#define SMIAPP_ALT_I2C_ADDR (0x6e >> 1) /* Alternate I2C Address */ + +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_CLOCK 0 +#define SMIAPP_CSI_SIGNALLING_MODE_CCP2_DATA_STROBE 1 +#define SMIAPP_CSI_SIGNALLING_MODE_CSI2 2 + +#define SMIAPP_NO_XSHUTDOWN -1 + +/* + * Sometimes due to board layout considerations the camera module can be + * mounted rotated. The typical rotation used is 180 degrees which can be + * corrected by giving a default H-FLIP and V-FLIP in the sensor readout. + * FIXME: rotation also changes the bayer pattern. + */ +enum smiapp_module_board_orient { + SMIAPP_MODULE_BOARD_ORIENT_0 = 0, + SMIAPP_MODULE_BOARD_ORIENT_180, +}; + +struct smiapp_flash_strobe_parms { + u8 mode; + u32 strobe_width_high_us; + u16 strobe_delay; + u16 stobe_start_point; + u8 trigger; +}; + +struct smiapp_platform_data { + /* + * Change the cci address if i2c_addr_alt is set. + * Both default and alternate cci addr need to be present + */ + unsigned short i2c_addr_dfl; /* Default i2c addr */ + unsigned short i2c_addr_alt; /* Alternate i2c addr */ + + unsigned int nvm_size; /* bytes */ + unsigned int ext_clk; /* sensor external clk */ + + unsigned int lanes; /* Number of CSI-2 lanes */ + u8 csi_signalling_mode; /* SMIAPP_CSI_SIGNALLING_MODE_* */ + const s64 *op_sys_clock; + + enum smiapp_module_board_orient module_board_orient; + + struct smiapp_flash_strobe_parms *strobe_setup; + + int (*set_xclk)(struct v4l2_subdev *sd, int hz); + char *ext_clk_name; + int xshutdown; /* gpio or SMIAPP_NO_XSHUTDOWN */ +}; + +#endif /* __SMIAPP_H_ */ diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h index cad374bdcf4b..d865dcf9879f 100644 --- a/include/media/soc_camera.h +++ b/include/media/soc_camera.h @@ -56,11 +56,15 @@ struct soc_camera_device { }; }; +/* Host supports programmable stride */ +#define SOCAM_HOST_CAP_STRIDE (1 << 0) + struct soc_camera_host { struct v4l2_device v4l2_dev; struct list_head list; struct mutex host_lock; /* Protect during probing */ unsigned char nr; /* Host number */ + u32 capabilities; void *priv; const char *drv_name; struct soc_camera_host_ops *ops; @@ -98,7 +102,7 @@ struct soc_camera_host_ops { int (*set_bus_param)(struct soc_camera_device *); int (*get_parm)(struct soc_camera_device *, struct v4l2_streamparm *); int (*set_parm)(struct soc_camera_device *, struct v4l2_streamparm *); - int (*enum_fsizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); + int (*enum_framesizes)(struct soc_camera_device *, struct v4l2_frmsizeenum *); unsigned int (*poll)(struct file *, poll_table *); }; diff --git a/include/media/soc_mediabus.h b/include/media/soc_mediabus.h index 73f1e7eb60f3..0dc6f4625b92 100644 --- a/include/media/soc_mediabus.h +++ b/include/media/soc_mediabus.h @@ -46,6 +46,24 @@ enum soc_mbus_order { SOC_MBUS_ORDER_BE, }; +/** + * enum soc_mbus_layout - planes layout in memory + * @SOC_MBUS_LAYOUT_PACKED: color components packed + * @SOC_MBUS_LAYOUT_PLANAR_2Y_U_V: YUV components stored in 3 planes (4:2:2) + * @SOC_MBUS_LAYOUT_PLANAR_2Y_C: YUV components stored in a luma and a + * chroma plane (C plane is half the size + * of Y plane) + * @SOC_MBUS_LAYOUT_PLANAR_Y_C: YUV components stored in a luma and a + * chroma plane (C plane is the same size + * as Y plane) + */ +enum soc_mbus_layout { + SOC_MBUS_LAYOUT_PACKED = 0, + SOC_MBUS_LAYOUT_PLANAR_2Y_U_V, + SOC_MBUS_LAYOUT_PLANAR_2Y_C, + SOC_MBUS_LAYOUT_PLANAR_Y_C, +}; + /** * struct soc_mbus_pixelfmt - Data format on the media bus * @name: Name of the format @@ -60,6 +78,7 @@ struct soc_mbus_pixelfmt { u32 fourcc; enum soc_mbus_packing packing; enum soc_mbus_order order; + enum soc_mbus_layout layout; u8 bits_per_sample; }; @@ -80,6 +99,8 @@ const struct soc_mbus_pixelfmt *soc_mbus_find_fmtdesc( const struct soc_mbus_pixelfmt *soc_mbus_get_fmtdesc( enum v4l2_mbus_pixelcode code); s32 soc_mbus_bytes_per_line(u32 width, const struct soc_mbus_pixelfmt *mf); +s32 soc_mbus_image_size(const struct soc_mbus_pixelfmt *mf, + u32 bytes_per_line, u32 height); int soc_mbus_samples_per_pixel(const struct soc_mbus_pixelfmt *mf, unsigned int *numerator, unsigned int *denominator); unsigned int soc_mbus_config_compatible(const struct v4l2_mbus_config *cfg, diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h index 11e67562b3ac..776605f1cbe2 100644 --- a/include/media/v4l2-ctrls.h +++ b/include/media/v4l2-ctrls.h @@ -25,6 +25,7 @@ #include /* forward references */ +struct file; struct v4l2_ctrl_handler; struct v4l2_ctrl_helper; struct v4l2_ctrl; @@ -129,7 +130,10 @@ struct v4l2_ctrl { u32 step; u32 menu_skip_mask; }; - const char * const *qmenu; + union { + const char * const *qmenu; + const s64 *qmenu_int; + }; unsigned long flags; union { s32 val; @@ -164,7 +168,9 @@ struct v4l2_ctrl_ref { /** struct v4l2_ctrl_handler - The control handler keeps track of all the * controls: both the controls owned by the handler and those inherited * from other handlers. + * @_lock: Default for "lock". * @lock: Lock to control access to this handler and its controls. + * May be replaced by the user right after init. * @ctrls: The list of controls owned by this handler. * @ctrl_refs: The list of control references. * @cached: The last found control reference. It is common that the same @@ -175,7 +181,8 @@ struct v4l2_ctrl_ref { * @error: The error code of the first failed control addition. */ struct v4l2_ctrl_handler { - struct mutex lock; + struct mutex _lock; + struct mutex *lock; struct list_head ctrls; struct list_head ctrl_refs; struct v4l2_ctrl_ref *cached; @@ -219,6 +226,7 @@ struct v4l2_ctrl_config { u32 flags; u32 menu_skip_mask; const char * const *qmenu; + const s64 *qmenu_int; unsigned int is_private:1; }; @@ -343,6 +351,23 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, s32 max, s32 mask, s32 def); +/** v4l2_ctrl_new_int_menu() - Create a new standard V4L2 integer menu control. + * @hdl: The control handler. + * @ops: The control ops. + * @id: The control ID. + * @max: The control's maximum value. + * @def: The control's default value. + * @qmenu_int: The control's menu entries. + * + * Same as v4l2_ctrl_new_std_menu(), but @mask is set to 0 and it additionaly + * takes as an argument an array of integers determining the menu items. + * + * If @id refers to a non-integer-menu control, then this function will return NULL. + */ +struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, + const struct v4l2_ctrl_ops *ops, + u32 id, s32 max, s32 def, const s64 *qmenu_int); + /** v4l2_ctrl_add_ctrl() - Add a control from another handler to this handler. * @hdl: The control handler. * @ctrl: The control to add. @@ -451,7 +476,7 @@ void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed); */ static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) { - mutex_lock(&ctrl->handler->lock); + mutex_lock(ctrl->handler->lock); } /** v4l2_ctrl_lock() - Helper function to unlock the handler @@ -460,7 +485,7 @@ static inline void v4l2_ctrl_lock(struct v4l2_ctrl *ctrl) */ static inline void v4l2_ctrl_unlock(struct v4l2_ctrl *ctrl) { - mutex_unlock(&ctrl->handler->lock); + mutex_unlock(ctrl->handler->lock); } /** v4l2_ctrl_g_ctrl() - Helper function to get the control's value from within a driver. @@ -487,10 +512,9 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl); int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val); /* Internal helper functions that deal with control events. */ -void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev); -void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl, - struct v4l2_subscribed_event *sev); +extern const struct v4l2_subscribed_event_ops v4l2_ctrl_sub_ev_ops; +void v4l2_ctrl_replace(struct v4l2_event *old, const struct v4l2_event *new); +void v4l2_ctrl_merge(const struct v4l2_event *old, struct v4l2_event *new); /* Can be used as a vidioc_log_status function that just dumps all controls associated with the filehandle. */ diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h index 96d22215cc88..a056e6ee1b68 100644 --- a/include/media/v4l2-dev.h +++ b/include/media/v4l2-dev.h @@ -39,6 +39,9 @@ struct v4l2_ctrl_handler; #define V4L2_FL_USES_V4L2_FH (1) /* Use the prio field of v4l2_fh for core priority checking */ #define V4L2_FL_USE_FH_PRIO (2) +/* If ioctl core locking is in use, then apply that also to all + file operations. Don't use this flag in new drivers! */ +#define V4L2_FL_LOCK_ALL_FOPS (3) /* Priority helper functions */ @@ -126,8 +129,10 @@ struct video_device /* ioctl callbacks */ const struct v4l2_ioctl_ops *ioctl_ops; + DECLARE_BITMAP(valid_ioctls, BASE_VIDIOC_PRIVATE); /* serialization lock */ + DECLARE_BITMAP(disable_locking, BASE_VIDIOC_PRIVATE); struct mutex *lock; }; @@ -173,6 +178,26 @@ void video_device_release(struct video_device *vdev); a dubious construction at best. */ void video_device_release_empty(struct video_device *vdev); +/* returns true if cmd is a known V4L2 ioctl */ +bool v4l2_is_known_ioctl(unsigned int cmd); + +/* mark that this command shouldn't use core locking */ +static inline void v4l2_disable_ioctl_locking(struct video_device *vdev, unsigned int cmd) +{ + if (_IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) + set_bit(_IOC_NR(cmd), vdev->disable_locking); +} + +/* Mark that this command isn't implemented. This must be called before + video_device_register. See also the comments in determine_valid_ioctls(). + This function allows drivers to provide just one v4l2_ioctl_ops struct, but + disable ioctls based on the specific card that is actually found. */ +static inline void v4l2_disable_ioctl(struct video_device *vdev, unsigned int cmd) +{ + if (_IOC_NR(cmd) < BASE_VIDIOC_PRIVATE) + set_bit(_IOC_NR(cmd), vdev->valid_ioctls); +} + /* helper functions to access driver private data. */ static inline void *video_get_drvdata(struct video_device *vdev) { diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index 5f14e8895ce2..2885a810a128 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h @@ -78,6 +78,19 @@ struct v4l2_kevent { struct v4l2_event event; }; +/** struct v4l2_subscribed_event_ops - Subscribed event operations. + * @add: Optional callback, called when a new listener is added + * @del: Optional callback, called when a listener stops listening + * @replace: Optional callback that can replace event 'old' with event 'new'. + * @merge: Optional callback that can merge event 'old' into event 'new'. + */ +struct v4l2_subscribed_event_ops { + int (*add)(struct v4l2_subscribed_event *sev, unsigned elems); + void (*del)(struct v4l2_subscribed_event *sev); + void (*replace)(struct v4l2_event *old, const struct v4l2_event *new); + void (*merge)(const struct v4l2_event *old, struct v4l2_event *new); +}; + /** struct v4l2_subscribed_event - Internal struct representing a subscribed event. * @list: List node for the v4l2_fh->subscribed list. * @type: Event type. @@ -85,8 +98,7 @@ struct v4l2_kevent { * @flags: Copy of v4l2_event_subscription->flags. * @fh: Filehandle that subscribed to this event. * @node: List node that hooks into the object's event list (if there is one). - * @replace: Optional callback that can replace event 'old' with event 'new'. - * @merge: Optional callback that can merge event 'old' into event 'new'. + * @ops: v4l2_subscribed_event_ops * @elems: The number of elements in the events array. * @first: The index of the events containing the oldest available event. * @in_use: The number of queued events. @@ -99,10 +111,7 @@ struct v4l2_subscribed_event { u32 flags; struct v4l2_fh *fh; struct list_head node; - void (*replace)(struct v4l2_event *old, - const struct v4l2_event *new); - void (*merge)(const struct v4l2_event *old, - struct v4l2_event *new); + const struct v4l2_subscribed_event_ops *ops; unsigned elems; unsigned first; unsigned in_use; @@ -115,7 +124,8 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev); void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev); int v4l2_event_pending(struct v4l2_fh *fh); int v4l2_event_subscribe(struct v4l2_fh *fh, - struct v4l2_event_subscription *sub, unsigned elems); + struct v4l2_event_subscription *sub, unsigned elems, + const struct v4l2_subscribed_event_ops *ops); int v4l2_event_unsubscribe(struct v4l2_fh *fh, struct v4l2_event_subscription *sub); void v4l2_event_unsubscribe_all(struct v4l2_fh *fh); diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h index 3cb939cd03f9..d8b76f7392f8 100644 --- a/include/media/v4l2-ioctl.h +++ b/include/media/v4l2-ioctl.h @@ -271,6 +271,12 @@ struct v4l2_ioctl_ops { struct v4l2_dv_timings *timings); int (*vidioc_g_dv_timings) (struct file *file, void *fh, struct v4l2_dv_timings *timings); + int (*vidioc_query_dv_timings) (struct file *file, void *fh, + struct v4l2_dv_timings *timings); + int (*vidioc_enum_dv_timings) (struct file *file, void *fh, + struct v4l2_enum_dv_timings *timings); + int (*vidioc_dv_timings_cap) (struct file *file, void *fh, + struct v4l2_dv_timings_cap *cap); int (*vidioc_subscribe_event) (struct v4l2_fh *fh, struct v4l2_event_subscription *sub); diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h index f0f3358d1b1b..c35a3545e273 100644 --- a/include/media/v4l2-subdev.h +++ b/include/media/v4l2-subdev.h @@ -307,6 +307,12 @@ struct v4l2_subdev_video_ops { struct v4l2_dv_timings *timings); int (*g_dv_timings)(struct v4l2_subdev *sd, struct v4l2_dv_timings *timings); + int (*enum_dv_timings)(struct v4l2_subdev *sd, + struct v4l2_enum_dv_timings *timings); + int (*query_dv_timings)(struct v4l2_subdev *sd, + struct v4l2_dv_timings *timings); + int (*dv_timings_cap)(struct v4l2_subdev *sd, + struct v4l2_dv_timings_cap *cap); int (*enum_mbus_fmt)(struct v4l2_subdev *sd, unsigned int index, enum v4l2_mbus_pixelcode *code); int (*enum_mbus_fsizes)(struct v4l2_subdev *sd, @@ -466,6 +472,15 @@ struct v4l2_subdev_pad_ops { struct v4l2_subdev_crop *crop); int (*get_crop)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, struct v4l2_subdev_crop *crop); + int (*get_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel); + int (*set_selection)(struct v4l2_subdev *sd, struct v4l2_subdev_fh *fh, + struct v4l2_subdev_selection *sel); +#ifdef CONFIG_MEDIA_CONTROLLER + int (*link_validate)(struct v4l2_subdev *sd, struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt); +#endif /* CONFIG_MEDIA_CONTROLLER */ }; struct v4l2_subdev_ops { @@ -541,7 +556,7 @@ struct v4l2_subdev { #define media_entity_to_v4l2_subdev(ent) \ container_of(ent, struct v4l2_subdev, entity) #define vdev_to_v4l2_subdev(vdev) \ - video_get_drvdata(vdev) + ((struct v4l2_subdev *)video_get_drvdata(vdev)) /* * Used for storing subdev information per file handle @@ -549,8 +564,11 @@ struct v4l2_subdev { struct v4l2_subdev_fh { struct v4l2_fh vfh; #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) - struct v4l2_mbus_framefmt *try_fmt; - struct v4l2_rect *try_crop; + struct { + struct v4l2_mbus_framefmt try_fmt; + struct v4l2_rect try_crop; + struct v4l2_rect try_compose; + } *pad; #endif }; @@ -558,17 +576,19 @@ struct v4l2_subdev_fh { container_of(fh, struct v4l2_subdev_fh, vfh) #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) -static inline struct v4l2_mbus_framefmt * -v4l2_subdev_get_try_format(struct v4l2_subdev_fh *fh, unsigned int pad) -{ - return &fh->try_fmt[pad]; -} +#define __V4L2_SUBDEV_MK_GET_TRY(rtype, fun_name, field_name) \ + static inline struct rtype * \ + v4l2_subdev_get_try_##fun_name(struct v4l2_subdev_fh *fh, \ + unsigned int pad) \ + { \ + BUG_ON(unlikely(pad >= vdev_to_v4l2_subdev( \ + fh->vfh.vdev)->entity.num_pads)); \ + return &fh->pad[pad].field_name; \ + } -static inline struct v4l2_rect * -v4l2_subdev_get_try_crop(struct v4l2_subdev_fh *fh, unsigned int pad) -{ - return &fh->try_crop[pad]; -} +__V4L2_SUBDEV_MK_GET_TRY(v4l2_mbus_framefmt, format, try_fmt) +__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, crop, try_compose) +__V4L2_SUBDEV_MK_GET_TRY(v4l2_rect, compose, try_compose) #endif extern const struct v4l2_file_operations v4l2_subdev_fops; @@ -593,6 +613,13 @@ static inline void *v4l2_get_subdev_hostdata(const struct v4l2_subdev *sd) return sd->host_priv; } +#ifdef CONFIG_MEDIA_CONTROLLER +int v4l2_subdev_link_validate_default(struct v4l2_subdev *sd, + struct media_link *link, + struct v4l2_subdev_format *source_fmt, + struct v4l2_subdev_format *sink_fmt); +int v4l2_subdev_link_validate(struct media_link *link); +#endif /* CONFIG_MEDIA_CONTROLLER */ void v4l2_subdev_init(struct v4l2_subdev *sd, const struct v4l2_subdev_ops *ops); diff --git a/include/media/videobuf-dma-contig.h b/include/media/videobuf-dma-contig.h index f0ed82543d9f..f473aeb86d3f 100644 --- a/include/media/videobuf-dma-contig.h +++ b/include/media/videobuf-dma-contig.h @@ -26,6 +26,16 @@ void videobuf_queue_dma_contig_init(struct videobuf_queue *q, void *priv, struct mutex *ext_lock); +void videobuf_queue_dma_contig_init_cached(struct videobuf_queue *q, + const struct videobuf_queue_ops *ops, + struct device *dev, + spinlock_t *irqlock, + enum v4l2_buf_type type, + enum v4l2_field field, + unsigned int msize, + void *priv, + struct mutex *ext_lock); + dma_addr_t videobuf_to_dma_contig(struct videobuf_buffer *buf); void videobuf_dma_contig_free(struct videobuf_queue *q, struct videobuf_buffer *buf); diff --git a/kernel/kfifo.c b/kernel/kfifo.c index c744b88c44e2..59dcf5b81d24 100644 --- a/kernel/kfifo.c +++ b/kernel/kfifo.c @@ -402,6 +402,7 @@ unsigned int __kfifo_max_r(unsigned int len, size_t recsize) return max; return len; } +EXPORT_SYMBOL(__kfifo_max_r); #define __KFIFO_PEEK(data, out, mask) \ ((data)[(out) & (mask)]) diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c index a63faec5e7fd..582aace20ea3 100644 --- a/sound/i2c/other/tea575x-tuner.c +++ b/sound/i2c/other/tea575x-tuner.c @@ -375,6 +375,9 @@ int snd_tea575x_init(struct snd_tea575x *tea) tea->vd.v4l2_dev = tea->v4l2_dev; tea->vd.ctrl_handler = &tea->ctrl_handler; set_bit(V4L2_FL_USE_FH_PRIO, &tea->vd.flags); + /* disable hw_freq_seek if we can't use it */ + if (tea->cannot_read_data) + v4l2_disable_ioctl(&tea->vd, VIDIOC_S_HW_FREQ_SEEK); v4l2_ctrl_handler_init(&tea->ctrl_handler, 1); v4l2_ctrl_new_std(&tea->ctrl_handler, &tea575x_ctrl_ops, V4L2_CID_AUDIO_MUTE, 0, 1, 1, 1);