diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/media/video/mx2_camera.c | 260 |
1 files changed, 256 insertions, 4 deletions
diff --git a/drivers/media/video/mx2_camera.c b/drivers/media/video/mx2_camera.c index 03be36e87077..18afaeeadb7b 100644 --- a/drivers/media/video/mx2_camera.c +++ b/drivers/media/video/mx2_camera.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <linux/dma-mapping.h> | 19 | #include <linux/dma-mapping.h> |
20 | #include <linux/errno.h> | 20 | #include <linux/errno.h> |
21 | #include <linux/fs.h> | 21 | #include <linux/fs.h> |
22 | #include <linux/gcd.h> | ||
22 | #include <linux/interrupt.h> | 23 | #include <linux/interrupt.h> |
23 | #include <linux/kernel.h> | 24 | #include <linux/kernel.h> |
24 | #include <linux/mm.h> | 25 | #include <linux/mm.h> |
@@ -204,8 +205,23 @@ | |||
204 | #define PRP_INTR_LBOVF (1 << 7) | 205 | #define PRP_INTR_LBOVF (1 << 7) |
205 | #define PRP_INTR_CH2OVF (1 << 8) | 206 | #define PRP_INTR_CH2OVF (1 << 8) |
206 | 207 | ||
208 | /* Resizing registers */ | ||
209 | #define PRP_RZ_VALID_TBL_LEN(x) ((x) << 24) | ||
210 | #define PRP_RZ_VALID_BILINEAR (1 << 31) | ||
211 | |||
207 | #define MAX_VIDEO_MEM 16 | 212 | #define MAX_VIDEO_MEM 16 |
208 | 213 | ||
214 | #define RESIZE_NUM_MIN 1 | ||
215 | #define RESIZE_NUM_MAX 20 | ||
216 | #define BC_COEF 3 | ||
217 | #define SZ_COEF (1 << BC_COEF) | ||
218 | |||
219 | #define RESIZE_DIR_H 0 | ||
220 | #define RESIZE_DIR_V 1 | ||
221 | |||
222 | #define RESIZE_ALGO_BILINEAR 0 | ||
223 | #define RESIZE_ALGO_AVERAGING 1 | ||
224 | |||
209 | struct mx2_prp_cfg { | 225 | struct mx2_prp_cfg { |
210 | int channel; | 226 | int channel; |
211 | u32 in_fmt; | 227 | u32 in_fmt; |
@@ -215,6 +231,13 @@ struct mx2_prp_cfg { | |||
215 | u32 irq_flags; | 231 | u32 irq_flags; |
216 | }; | 232 | }; |
217 | 233 | ||
234 | /* prp resizing parameters */ | ||
235 | struct emma_prp_resize { | ||
236 | int algo; /* type of algorithm used */ | ||
237 | int len; /* number of coefficients */ | ||
238 | unsigned char s[RESIZE_NUM_MAX]; /* table of coefficients */ | ||
239 | }; | ||
240 | |||
218 | /* prp configuration for a client-host fmt pair */ | 241 | /* prp configuration for a client-host fmt pair */ |
219 | struct mx2_fmt_cfg { | 242 | struct mx2_fmt_cfg { |
220 | enum v4l2_mbus_pixelcode in_fmt; | 243 | enum v4l2_mbus_pixelcode in_fmt; |
@@ -274,6 +297,8 @@ struct mx2_camera_dev { | |||
274 | dma_addr_t discard_buffer_dma; | 297 | dma_addr_t discard_buffer_dma; |
275 | size_t discard_size; | 298 | size_t discard_size; |
276 | struct mx2_fmt_cfg *emma_prp; | 299 | struct mx2_fmt_cfg *emma_prp; |
300 | struct emma_prp_resize resizing[2]; | ||
301 | unsigned int s_width, s_height; | ||
277 | u32 frame_count; | 302 | u32 frame_count; |
278 | struct vb2_alloc_ctx *alloc_ctx; | 303 | struct vb2_alloc_ctx *alloc_ctx; |
279 | }; | 304 | }; |
@@ -678,7 +703,7 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, | |||
678 | struct mx2_camera_dev *pcdev = ici->priv; | 703 | struct mx2_camera_dev *pcdev = ici->priv; |
679 | struct mx2_fmt_cfg *prp = pcdev->emma_prp; | 704 | struct mx2_fmt_cfg *prp = pcdev->emma_prp; |
680 | 705 | ||
681 | writel((icd->user_width << 16) | icd->user_height, | 706 | writel((pcdev->s_width << 16) | pcdev->s_height, |
682 | pcdev->base_emma + PRP_SRC_FRAME_SIZE); | 707 | pcdev->base_emma + PRP_SRC_FRAME_SIZE); |
683 | writel(prp->cfg.src_pixel, | 708 | writel(prp->cfg.src_pixel, |
684 | pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); | 709 | pcdev->base_emma + PRP_SRC_PIXEL_FORMAT_CNTL); |
@@ -698,6 +723,74 @@ static void mx27_camera_emma_buf_init(struct soc_camera_device *icd, | |||
698 | writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); | 723 | writel(prp->cfg.irq_flags, pcdev->base_emma + PRP_INTR_CNTL); |
699 | } | 724 | } |
700 | 725 | ||
726 | static void mx2_prp_resize_commit(struct mx2_camera_dev *pcdev) | ||
727 | { | ||
728 | int dir; | ||
729 | |||
730 | for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { | ||
731 | unsigned char *s = pcdev->resizing[dir].s; | ||
732 | int len = pcdev->resizing[dir].len; | ||
733 | unsigned int coeff[2] = {0, 0}; | ||
734 | unsigned int valid = 0; | ||
735 | int i; | ||
736 | |||
737 | if (len == 0) | ||
738 | continue; | ||
739 | |||
740 | for (i = RESIZE_NUM_MAX - 1; i >= 0; i--) { | ||
741 | int j; | ||
742 | |||
743 | j = i > 9 ? 1 : 0; | ||
744 | coeff[j] = (coeff[j] << BC_COEF) | | ||
745 | (s[i] & (SZ_COEF - 1)); | ||
746 | |||
747 | if (i == 5 || i == 15) | ||
748 | coeff[j] <<= 1; | ||
749 | |||
750 | valid = (valid << 1) | (s[i] >> BC_COEF); | ||
751 | } | ||
752 | |||
753 | valid |= PRP_RZ_VALID_TBL_LEN(len); | ||
754 | |||
755 | if (pcdev->resizing[dir].algo == RESIZE_ALGO_BILINEAR) | ||
756 | valid |= PRP_RZ_VALID_BILINEAR; | ||
757 | |||
758 | if (pcdev->emma_prp->cfg.channel == 1) { | ||
759 | if (dir == RESIZE_DIR_H) { | ||
760 | writel(coeff[0], pcdev->base_emma + | ||
761 | PRP_CH1_RZ_HORI_COEF1); | ||
762 | writel(coeff[1], pcdev->base_emma + | ||
763 | PRP_CH1_RZ_HORI_COEF2); | ||
764 | writel(valid, pcdev->base_emma + | ||
765 | PRP_CH1_RZ_HORI_VALID); | ||
766 | } else { | ||
767 | writel(coeff[0], pcdev->base_emma + | ||
768 | PRP_CH1_RZ_VERT_COEF1); | ||
769 | writel(coeff[1], pcdev->base_emma + | ||
770 | PRP_CH1_RZ_VERT_COEF2); | ||
771 | writel(valid, pcdev->base_emma + | ||
772 | PRP_CH1_RZ_VERT_VALID); | ||
773 | } | ||
774 | } else { | ||
775 | if (dir == RESIZE_DIR_H) { | ||
776 | writel(coeff[0], pcdev->base_emma + | ||
777 | PRP_CH2_RZ_HORI_COEF1); | ||
778 | writel(coeff[1], pcdev->base_emma + | ||
779 | PRP_CH2_RZ_HORI_COEF2); | ||
780 | writel(valid, pcdev->base_emma + | ||
781 | PRP_CH2_RZ_HORI_VALID); | ||
782 | } else { | ||
783 | writel(coeff[0], pcdev->base_emma + | ||
784 | PRP_CH2_RZ_VERT_COEF1); | ||
785 | writel(coeff[1], pcdev->base_emma + | ||
786 | PRP_CH2_RZ_VERT_COEF2); | ||
787 | writel(valid, pcdev->base_emma + | ||
788 | PRP_CH2_RZ_VERT_VALID); | ||
789 | } | ||
790 | } | ||
791 | } | ||
792 | } | ||
793 | |||
701 | static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) | 794 | static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) |
702 | { | 795 | { |
703 | struct soc_camera_device *icd = soc_camera_from_vb2q(q); | 796 | struct soc_camera_device *icd = soc_camera_from_vb2q(q); |
@@ -764,6 +857,8 @@ static int mx2_start_streaming(struct vb2_queue *q, unsigned int count) | |||
764 | list_add_tail(&pcdev->buf_discard[1].queue, | 857 | list_add_tail(&pcdev->buf_discard[1].queue, |
765 | &pcdev->discard); | 858 | &pcdev->discard); |
766 | 859 | ||
860 | mx2_prp_resize_commit(pcdev); | ||
861 | |||
767 | mx27_camera_emma_buf_init(icd, bytesperline); | 862 | mx27_camera_emma_buf_init(icd, bytesperline); |
768 | 863 | ||
769 | if (prp->cfg.channel == 1) { | 864 | if (prp->cfg.channel == 1) { |
@@ -1049,6 +1144,123 @@ static int mx2_camera_get_formats(struct soc_camera_device *icd, | |||
1049 | return formats; | 1144 | return formats; |
1050 | } | 1145 | } |
1051 | 1146 | ||
1147 | static int mx2_emmaprp_resize(struct mx2_camera_dev *pcdev, | ||
1148 | struct v4l2_mbus_framefmt *mf_in, | ||
1149 | struct v4l2_pix_format *pix_out, bool apply) | ||
1150 | { | ||
1151 | int num, den; | ||
1152 | unsigned long m; | ||
1153 | int i, dir; | ||
1154 | |||
1155 | for (dir = RESIZE_DIR_H; dir <= RESIZE_DIR_V; dir++) { | ||
1156 | struct emma_prp_resize tmprsz; | ||
1157 | unsigned char *s = tmprsz.s; | ||
1158 | int len = 0; | ||
1159 | int in, out; | ||
1160 | |||
1161 | if (dir == RESIZE_DIR_H) { | ||
1162 | in = mf_in->width; | ||
1163 | out = pix_out->width; | ||
1164 | } else { | ||
1165 | in = mf_in->height; | ||
1166 | out = pix_out->height; | ||
1167 | } | ||
1168 | |||
1169 | if (in < out) | ||
1170 | return -EINVAL; | ||
1171 | else if (in == out) | ||
1172 | continue; | ||
1173 | |||
1174 | /* Calculate ratio */ | ||
1175 | m = gcd(in, out); | ||
1176 | num = in / m; | ||
1177 | den = out / m; | ||
1178 | if (num > RESIZE_NUM_MAX) | ||
1179 | return -EINVAL; | ||
1180 | |||
1181 | if ((num >= 2 * den) && (den == 1) && | ||
1182 | (num < 9) && (!(num & 0x01))) { | ||
1183 | int sum = 0; | ||
1184 | int j; | ||
1185 | |||
1186 | /* Average scaling for >= 2:1 ratios */ | ||
1187 | /* Support can be added for num >=9 and odd values */ | ||
1188 | |||
1189 | tmprsz.algo = RESIZE_ALGO_AVERAGING; | ||
1190 | len = num; | ||
1191 | |||
1192 | for (i = 0; i < (len / 2); i++) | ||
1193 | s[i] = 8; | ||
1194 | |||
1195 | do { | ||
1196 | for (i = 0; i < (len / 2); i++) { | ||
1197 | s[i] = s[i] >> 1; | ||
1198 | sum = 0; | ||
1199 | for (j = 0; j < (len / 2); j++) | ||
1200 | sum += s[j]; | ||
1201 | if (sum == 4) | ||
1202 | break; | ||
1203 | } | ||
1204 | } while (sum != 4); | ||
1205 | |||
1206 | for (i = (len / 2); i < len; i++) | ||
1207 | s[i] = s[len - i - 1]; | ||
1208 | |||
1209 | s[len - 1] |= SZ_COEF; | ||
1210 | } else { | ||
1211 | /* bilinear scaling for < 2:1 ratios */ | ||
1212 | int v; /* overflow counter */ | ||
1213 | int coeff, nxt; /* table output */ | ||
1214 | int in_pos_inc = 2 * den; | ||
1215 | int out_pos = num; | ||
1216 | int out_pos_inc = 2 * num; | ||
1217 | int init_carry = num - den; | ||
1218 | int carry = init_carry; | ||
1219 | |||
1220 | tmprsz.algo = RESIZE_ALGO_BILINEAR; | ||
1221 | v = den + in_pos_inc; | ||
1222 | do { | ||
1223 | coeff = v - out_pos; | ||
1224 | out_pos += out_pos_inc; | ||
1225 | carry += out_pos_inc; | ||
1226 | for (nxt = 0; v < out_pos; nxt++) { | ||
1227 | v += in_pos_inc; | ||
1228 | carry -= in_pos_inc; | ||
1229 | } | ||
1230 | |||
1231 | if (len > RESIZE_NUM_MAX) | ||
1232 | return -EINVAL; | ||
1233 | |||
1234 | coeff = ((coeff << BC_COEF) + | ||
1235 | (in_pos_inc >> 1)) / in_pos_inc; | ||
1236 | |||
1237 | if (coeff >= (SZ_COEF - 1)) | ||
1238 | coeff--; | ||
1239 | |||
1240 | coeff |= SZ_COEF; | ||
1241 | s[len] = (unsigned char)coeff; | ||
1242 | len++; | ||
1243 | |||
1244 | for (i = 1; i < nxt; i++) { | ||
1245 | if (len >= RESIZE_NUM_MAX) | ||
1246 | return -EINVAL; | ||
1247 | s[len] = 0; | ||
1248 | len++; | ||
1249 | } | ||
1250 | } while (carry != init_carry); | ||
1251 | } | ||
1252 | tmprsz.len = len; | ||
1253 | if (dir == RESIZE_DIR_H) | ||
1254 | mf_in->width = pix_out->width; | ||
1255 | else | ||
1256 | mf_in->height = pix_out->height; | ||
1257 | |||
1258 | if (apply) | ||
1259 | memcpy(&pcdev->resizing[dir], &tmprsz, sizeof(tmprsz)); | ||
1260 | } | ||
1261 | return 0; | ||
1262 | } | ||
1263 | |||
1052 | static int mx2_camera_set_fmt(struct soc_camera_device *icd, | 1264 | static int mx2_camera_set_fmt(struct soc_camera_device *icd, |
1053 | struct v4l2_format *f) | 1265 | struct v4l2_format *f) |
1054 | { | 1266 | { |
@@ -1060,6 +1272,9 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, | |||
1060 | struct v4l2_mbus_framefmt mf; | 1272 | struct v4l2_mbus_framefmt mf; |
1061 | int ret; | 1273 | int ret; |
1062 | 1274 | ||
1275 | dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", | ||
1276 | __func__, pix->width, pix->height); | ||
1277 | |||
1063 | xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); | 1278 | xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat); |
1064 | if (!xlate) { | 1279 | if (!xlate) { |
1065 | dev_warn(icd->parent, "Format %x not found\n", | 1280 | dev_warn(icd->parent, "Format %x not found\n", |
@@ -1077,6 +1292,22 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, | |||
1077 | if (ret < 0 && ret != -ENOIOCTLCMD) | 1292 | if (ret < 0 && ret != -ENOIOCTLCMD) |
1078 | return ret; | 1293 | return ret; |
1079 | 1294 | ||
1295 | /* Store width and height returned by the sensor for resizing */ | ||
1296 | pcdev->s_width = mf.width; | ||
1297 | pcdev->s_height = mf.height; | ||
1298 | dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", | ||
1299 | __func__, pcdev->s_width, pcdev->s_height); | ||
1300 | |||
1301 | pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, | ||
1302 | xlate->host_fmt->fourcc); | ||
1303 | |||
1304 | memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); | ||
1305 | if ((mf.width != pix->width || mf.height != pix->height) && | ||
1306 | pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { | ||
1307 | if (mx2_emmaprp_resize(pcdev, &mf, pix, true) < 0) | ||
1308 | dev_dbg(icd->parent, "%s: can't resize\n", __func__); | ||
1309 | } | ||
1310 | |||
1080 | if (mf.code != xlate->code) | 1311 | if (mf.code != xlate->code) |
1081 | return -EINVAL; | 1312 | return -EINVAL; |
1082 | 1313 | ||
@@ -1086,9 +1317,8 @@ static int mx2_camera_set_fmt(struct soc_camera_device *icd, | |||
1086 | pix->colorspace = mf.colorspace; | 1317 | pix->colorspace = mf.colorspace; |
1087 | icd->current_fmt = xlate; | 1318 | icd->current_fmt = xlate; |
1088 | 1319 | ||
1089 | if (cpu_is_mx27()) | 1320 | dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", |
1090 | pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, | 1321 | __func__, pix->width, pix->height); |
1091 | xlate->host_fmt->fourcc); | ||
1092 | 1322 | ||
1093 | return 0; | 1323 | return 0; |
1094 | } | 1324 | } |
@@ -1101,9 +1331,14 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, | |||
1101 | struct v4l2_pix_format *pix = &f->fmt.pix; | 1331 | struct v4l2_pix_format *pix = &f->fmt.pix; |
1102 | struct v4l2_mbus_framefmt mf; | 1332 | struct v4l2_mbus_framefmt mf; |
1103 | __u32 pixfmt = pix->pixelformat; | 1333 | __u32 pixfmt = pix->pixelformat; |
1334 | struct soc_camera_host *ici = to_soc_camera_host(icd->parent); | ||
1335 | struct mx2_camera_dev *pcdev = ici->priv; | ||
1104 | unsigned int width_limit; | 1336 | unsigned int width_limit; |
1105 | int ret; | 1337 | int ret; |
1106 | 1338 | ||
1339 | dev_dbg(icd->parent, "%s: requested params: width = %d, height = %d\n", | ||
1340 | __func__, pix->width, pix->height); | ||
1341 | |||
1107 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); | 1342 | xlate = soc_camera_xlate_by_fourcc(icd, pixfmt); |
1108 | if (pixfmt && !xlate) { | 1343 | if (pixfmt && !xlate) { |
1109 | dev_warn(icd->parent, "Format %x not found\n", pixfmt); | 1344 | dev_warn(icd->parent, "Format %x not found\n", pixfmt); |
@@ -1153,6 +1388,20 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, | |||
1153 | if (ret < 0) | 1388 | if (ret < 0) |
1154 | return ret; | 1389 | return ret; |
1155 | 1390 | ||
1391 | dev_dbg(icd->parent, "%s: sensor params: width = %d, height = %d\n", | ||
1392 | __func__, pcdev->s_width, pcdev->s_height); | ||
1393 | |||
1394 | /* If the sensor does not support image size try PrP resizing */ | ||
1395 | pcdev->emma_prp = mx27_emma_prp_get_format(xlate->code, | ||
1396 | xlate->host_fmt->fourcc); | ||
1397 | |||
1398 | memset(pcdev->resizing, 0, sizeof(pcdev->resizing)); | ||
1399 | if ((mf.width != pix->width || mf.height != pix->height) && | ||
1400 | pcdev->emma_prp->cfg.in_fmt == PRP_CNTL_DATA_IN_YUV422) { | ||
1401 | if (mx2_emmaprp_resize(pcdev, &mf, pix, false) < 0) | ||
1402 | dev_dbg(icd->parent, "%s: can't resize\n", __func__); | ||
1403 | } | ||
1404 | |||
1156 | if (mf.field == V4L2_FIELD_ANY) | 1405 | if (mf.field == V4L2_FIELD_ANY) |
1157 | mf.field = V4L2_FIELD_NONE; | 1406 | mf.field = V4L2_FIELD_NONE; |
1158 | /* | 1407 | /* |
@@ -1171,6 +1420,9 @@ static int mx2_camera_try_fmt(struct soc_camera_device *icd, | |||
1171 | pix->field = mf.field; | 1420 | pix->field = mf.field; |
1172 | pix->colorspace = mf.colorspace; | 1421 | pix->colorspace = mf.colorspace; |
1173 | 1422 | ||
1423 | dev_dbg(icd->parent, "%s: returned params: width = %d, height = %d\n", | ||
1424 | __func__, pix->width, pix->height); | ||
1425 | |||
1174 | return 0; | 1426 | return 0; |
1175 | } | 1427 | } |
1176 | 1428 | ||