diff options
author | Jean-François Moine <moinejf@free.fr> | 2012-02-27 05:48:32 -0500 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-03-08 06:03:18 -0500 |
commit | f1801904eeb79f46e7854e4d18c7107160a16093 (patch) | |
tree | 402d96a017ea7614fe92d04a5a60b1f0af4f566a /drivers/media/video/gspca/zc3xx.c | |
parent | 30c73d464a10bee4bb8375bb2ed8cc102c507bb7 (diff) |
[media] gspca - zc3xx: Do automatic transfer control for hv7131r and pas202b
The bridge register 11 reports the current transfer status.
This value is used to know about a possible overflow and to adjust
the transfer parameters (registers 07 and 08).
Signed-off-by: Jean-François Moine <moinejf@free.fr>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media/video/gspca/zc3xx.c')
-rw-r--r-- | drivers/media/video/gspca/zc3xx.c | 134 |
1 files changed, 134 insertions, 0 deletions
diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c index c02fb31987cc..d6175f5d1056 100644 --- a/drivers/media/video/gspca/zc3xx.c +++ b/drivers/media/video/gspca/zc3xx.c | |||
@@ -57,6 +57,9 @@ struct sd { | |||
57 | 57 | ||
58 | struct gspca_ctrl ctrls[NCTRLS]; | 58 | struct gspca_ctrl ctrls[NCTRLS]; |
59 | 59 | ||
60 | struct work_struct work; | ||
61 | struct workqueue_struct *work_thread; | ||
62 | |||
60 | u8 reg08; /* webcam compression quality */ | 63 | u8 reg08; /* webcam compression quality */ |
61 | 64 | ||
62 | u8 bridge; | 65 | u8 bridge; |
@@ -5932,6 +5935,9 @@ static void setquality(struct gspca_dev *gspca_dev) | |||
5932 | case SENSOR_OV7620: | 5935 | case SENSOR_OV7620: |
5933 | reg07 = 0x30; | 5936 | reg07 = 0x30; |
5934 | break; | 5937 | break; |
5938 | case SENSOR_HV7131R: | ||
5939 | case SENSOR_PAS202B: | ||
5940 | return; /* done by work queue */ | ||
5935 | } | 5941 | } |
5936 | reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); | 5942 | reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); |
5937 | if (reg07 != 0) | 5943 | if (reg07 != 0) |
@@ -6069,6 +6075,114 @@ static void setautogain(struct gspca_dev *gspca_dev) | |||
6069 | reg_w(gspca_dev, autoval, 0x0180); | 6075 | reg_w(gspca_dev, autoval, 0x0180); |
6070 | } | 6076 | } |
6071 | 6077 | ||
6078 | /* update the transfer parameters */ | ||
6079 | /* This function is executed from a work queue. */ | ||
6080 | /* The exact use of the bridge registers 07 and 08 is not known. | ||
6081 | * The following algorithm has been adapted from ms-win traces */ | ||
6082 | static void transfer_update(struct work_struct *work) | ||
6083 | { | ||
6084 | struct sd *sd = container_of(work, struct sd, work); | ||
6085 | struct gspca_dev *gspca_dev = &sd->gspca_dev; | ||
6086 | int change, good; | ||
6087 | u8 reg07, reg11; | ||
6088 | |||
6089 | /* synchronize with the main driver and initialize the registers */ | ||
6090 | mutex_lock(&gspca_dev->usb_lock); | ||
6091 | reg07 = 0; /* max */ | ||
6092 | reg_w(gspca_dev, reg07, 0x0007); | ||
6093 | reg_w(gspca_dev, sd->reg08, ZC3XX_R008_CLOCKSETTING); | ||
6094 | mutex_unlock(&gspca_dev->usb_lock); | ||
6095 | |||
6096 | good = 0; | ||
6097 | for (;;) { | ||
6098 | msleep(100); | ||
6099 | |||
6100 | /* get the transfer status */ | ||
6101 | /* the bit 0 of the bridge register 11 indicates overflow */ | ||
6102 | mutex_lock(&gspca_dev->usb_lock); | ||
6103 | if (!gspca_dev->present || !gspca_dev->streaming) | ||
6104 | goto err; | ||
6105 | reg11 = reg_r(gspca_dev, 0x0011); | ||
6106 | if (gspca_dev->usb_err < 0 | ||
6107 | || !gspca_dev->present || !gspca_dev->streaming) | ||
6108 | goto err; | ||
6109 | |||
6110 | change = reg11 & 0x01; | ||
6111 | if (change) { /* overflow */ | ||
6112 | switch (reg07) { | ||
6113 | case 0: /* max */ | ||
6114 | reg07 = sd->sensor == SENSOR_HV7131R | ||
6115 | ? 0x30 : 0x32; | ||
6116 | if (sd->reg08 != 0) { | ||
6117 | change = 3; | ||
6118 | sd->reg08--; | ||
6119 | } | ||
6120 | break; | ||
6121 | case 0x32: | ||
6122 | reg07 -= 4; | ||
6123 | break; | ||
6124 | default: | ||
6125 | reg07 -= 2; | ||
6126 | break; | ||
6127 | case 2: | ||
6128 | change = 0; /* already min */ | ||
6129 | break; | ||
6130 | } | ||
6131 | good = 0; | ||
6132 | } else { /* no overflow */ | ||
6133 | if (reg07 != 0) { /* if not max */ | ||
6134 | good++; | ||
6135 | if (good >= 10) { | ||
6136 | good = 0; | ||
6137 | change = 1; | ||
6138 | reg07 += 2; | ||
6139 | switch (reg07) { | ||
6140 | case 0x30: | ||
6141 | if (sd->sensor == SENSOR_PAS202B) | ||
6142 | reg07 += 2; | ||
6143 | break; | ||
6144 | case 0x32: | ||
6145 | case 0x34: | ||
6146 | reg07 = 0; | ||
6147 | break; | ||
6148 | } | ||
6149 | } | ||
6150 | } else { /* reg07 max */ | ||
6151 | if (sd->reg08 < sizeof jpeg_qual - 1) { | ||
6152 | good++; | ||
6153 | if (good > 10) { | ||
6154 | sd->reg08++; | ||
6155 | change = 2; | ||
6156 | } | ||
6157 | } | ||
6158 | } | ||
6159 | } | ||
6160 | if (change) { | ||
6161 | if (change & 1) { | ||
6162 | reg_w(gspca_dev, reg07, 0x0007); | ||
6163 | if (gspca_dev->usb_err < 0 | ||
6164 | || !gspca_dev->present | ||
6165 | || !gspca_dev->streaming) | ||
6166 | goto err; | ||
6167 | } | ||
6168 | if (change & 2) { | ||
6169 | reg_w(gspca_dev, sd->reg08, | ||
6170 | ZC3XX_R008_CLOCKSETTING); | ||
6171 | if (gspca_dev->usb_err < 0 | ||
6172 | || !gspca_dev->present | ||
6173 | || !gspca_dev->streaming) | ||
6174 | goto err; | ||
6175 | jpeg_set_qual(sd->jpeg_hdr, | ||
6176 | jpeg_qual[sd->reg08]); | ||
6177 | } | ||
6178 | } | ||
6179 | mutex_unlock(&gspca_dev->usb_lock); | ||
6180 | } | ||
6181 | return; | ||
6182 | err: | ||
6183 | mutex_unlock(&gspca_dev->usb_lock); | ||
6184 | } | ||
6185 | |||
6072 | static void send_unknown(struct gspca_dev *gspca_dev, int sensor) | 6186 | static void send_unknown(struct gspca_dev *gspca_dev, int sensor) |
6073 | { | 6187 | { |
6074 | reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */ | 6188 | reg_w(gspca_dev, 0x01, 0x0000); /* bridge reset */ |
@@ -6398,6 +6512,8 @@ static int sd_config(struct gspca_dev *gspca_dev, | |||
6398 | gspca_dev->cam.ctrls = sd->ctrls; | 6512 | gspca_dev->cam.ctrls = sd->ctrls; |
6399 | sd->reg08 = REG08_DEF; | 6513 | sd->reg08 = REG08_DEF; |
6400 | 6514 | ||
6515 | INIT_WORK(&sd->work, transfer_update); | ||
6516 | |||
6401 | return 0; | 6517 | return 0; |
6402 | } | 6518 | } |
6403 | 6519 | ||
@@ -6807,6 +6923,18 @@ static int sd_start(struct gspca_dev *gspca_dev) | |||
6807 | } | 6923 | } |
6808 | 6924 | ||
6809 | setautogain(gspca_dev); | 6925 | setautogain(gspca_dev); |
6926 | |||
6927 | /* start the transfer update thread if needed */ | ||
6928 | if (gspca_dev->usb_err >= 0) { | ||
6929 | switch (sd->sensor) { | ||
6930 | case SENSOR_HV7131R: | ||
6931 | case SENSOR_PAS202B: | ||
6932 | sd->work_thread = create_singlethread_workqueue(MODULE_NAME); | ||
6933 | queue_work(sd->work_thread, &sd->work); | ||
6934 | break; | ||
6935 | } | ||
6936 | } | ||
6937 | |||
6810 | return gspca_dev->usb_err; | 6938 | return gspca_dev->usb_err; |
6811 | } | 6939 | } |
6812 | 6940 | ||
@@ -6815,6 +6943,12 @@ static void sd_stop0(struct gspca_dev *gspca_dev) | |||
6815 | { | 6943 | { |
6816 | struct sd *sd = (struct sd *) gspca_dev; | 6944 | struct sd *sd = (struct sd *) gspca_dev; |
6817 | 6945 | ||
6946 | if (sd->work_thread != NULL) { | ||
6947 | mutex_unlock(&gspca_dev->usb_lock); | ||
6948 | destroy_workqueue(sd->work_thread); | ||
6949 | mutex_lock(&gspca_dev->usb_lock); | ||
6950 | sd->work_thread = NULL; | ||
6951 | } | ||
6818 | if (!gspca_dev->present) | 6952 | if (!gspca_dev->present) |
6819 | return; | 6953 | return; |
6820 | send_unknown(gspca_dev, sd->sensor); | 6954 | send_unknown(gspca_dev, sd->sensor); |