1 /*
2  *  Driver for the Conexant CX23885/7/8 PCIe bridge
3  *
4  *  Various common ioctl() support functions
5  *
6  *  Copyright (c) 2009 Andy Walls <awalls@md.metrocast.net>
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22  */
23 
24 #include "cx23885.h"
25 #include "cx23885-ioctl.h"
26 
27 #include <media/v4l2-chip-ident.h>
28 
29 int cx23885_g_chip_ident(struct file *file, void *fh,
30 			 struct v4l2_dbg_chip_ident *chip)
31 {
32 	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
33 	int err = 0;
34 	u8 rev;
35 
36 	chip->ident = V4L2_IDENT_NONE;
37 	chip->revision = 0;
38 	switch (chip->match.type) {
39 	case V4L2_CHIP_MATCH_HOST:
40 		switch (chip->match.addr) {
41 		case 0:
42 			rev = cx_read(RDR_CFG2) & 0xff;
43 			switch (dev->pci->device) {
44 			case 0x8852:
45 				/* rev 0x04 could be '885 or '888. Pick '888. */
46 				if (rev == 0x04)
47 					chip->ident = V4L2_IDENT_CX23888;
48 				else
49 					chip->ident = V4L2_IDENT_CX23885;
50 				break;
51 			case 0x8880:
52 				if (rev == 0x0e || rev == 0x0f)
53 					chip->ident = V4L2_IDENT_CX23887;
54 				else
55 					chip->ident = V4L2_IDENT_CX23888;
56 				break;
57 			default:
58 				chip->ident = V4L2_IDENT_UNKNOWN;
59 				break;
60 			}
61 			chip->revision = (dev->pci->device << 16) | (rev << 8) |
62 					 (dev->hwrevision & 0xff);
63 			break;
64 		case 1:
65 			if (dev->v4l_device != NULL) {
66 				chip->ident = V4L2_IDENT_CX23417;
67 				chip->revision = 0;
68 			}
69 			break;
70 		case 2:
71 			/*
72 			 * The integrated IR controller on the CX23888 is
73 			 * host chip 2.  It may not be used/initialized or sd_ir
74 			 * may be pointing at the cx25840 subdevice for the
75 			 * IR controller on the CX23885.  Thus we find it
76 			 * without using the dev->sd_ir pointer.
77 			 */
78 			call_hw(dev, CX23885_HW_888_IR, core, g_chip_ident,
79 				chip);
80 			break;
81 		default:
82 			err = -EINVAL; /* per V4L2 spec */
83 			break;
84 		}
85 		break;
86 	case V4L2_CHIP_MATCH_I2C_DRIVER:
87 		/* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
88 		call_all(dev, core, g_chip_ident, chip);
89 		break;
90 	case V4L2_CHIP_MATCH_I2C_ADDR:
91 		/*
92 		 * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
93 		 * to look if a chip is at the address with no driver.  That's a
94 		 * dangerous thing to do with EEPROMs anyway.
95 		 */
96 		call_all(dev, core, g_chip_ident, chip);
97 		break;
98 	default:
99 		err = -EINVAL;
100 		break;
101 	}
102 	return err;
103 }
104 
105 #ifdef CONFIG_VIDEO_ADV_DEBUG
106 static int cx23885_g_host_register(struct cx23885_dev *dev,
107 				   struct v4l2_dbg_register *reg)
108 {
109 	if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
110 		return -EINVAL;
111 
112 	reg->size = 4;
113 	reg->val = cx_read(reg->reg);
114 	return 0;
115 }
116 
117 static int cx23417_g_register(struct cx23885_dev *dev,
118 			      struct v4l2_dbg_register *reg)
119 {
120 	u32 value;
121 
122 	if (dev->v4l_device == NULL)
123 		return -EINVAL;
124 
125 	if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
126 		return -EINVAL;
127 
128 	if (mc417_register_read(dev, (u16) reg->reg, &value))
129 		return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
130 
131 	reg->size = 4;
132 	reg->val = value;
133 	return 0;
134 }
135 
136 int cx23885_g_register(struct file *file, void *fh,
137 		       struct v4l2_dbg_register *reg)
138 {
139 	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
140 
141 	if (!capable(CAP_SYS_ADMIN))
142 		return -EPERM;
143 
144 	if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
145 		switch (reg->match.addr) {
146 		case 0:
147 			return cx23885_g_host_register(dev, reg);
148 		case 1:
149 			return cx23417_g_register(dev, reg);
150 		default:
151 			break;
152 		}
153 	}
154 
155 	/* FIXME - any error returns should not be ignored */
156 	call_all(dev, core, g_register, reg);
157 	return 0;
158 }
159 
160 static int cx23885_s_host_register(struct cx23885_dev *dev,
161 				   struct v4l2_dbg_register *reg)
162 {
163 	if ((reg->reg & 0x3) != 0 || reg->reg >= pci_resource_len(dev->pci, 0))
164 		return -EINVAL;
165 
166 	reg->size = 4;
167 	cx_write(reg->reg, reg->val);
168 	return 0;
169 }
170 
171 static int cx23417_s_register(struct cx23885_dev *dev,
172 			      struct v4l2_dbg_register *reg)
173 {
174 	if (dev->v4l_device == NULL)
175 		return -EINVAL;
176 
177 	if ((reg->reg & 0x3) != 0 || reg->reg >= 0x10000)
178 		return -EINVAL;
179 
180 	if (mc417_register_write(dev, (u16) reg->reg, (u32) reg->val))
181 		return -EINVAL; /* V4L2 spec, but -EREMOTEIO really */
182 
183 	reg->size = 4;
184 	return 0;
185 }
186 
187 int cx23885_s_register(struct file *file, void *fh,
188 		       struct v4l2_dbg_register *reg)
189 {
190 	struct cx23885_dev *dev = ((struct cx23885_fh *)fh)->dev;
191 
192 	if (!capable(CAP_SYS_ADMIN))
193 		return -EPERM;
194 
195 	if (reg->match.type == V4L2_CHIP_MATCH_HOST) {
196 		switch (reg->match.addr) {
197 		case 0:
198 			return cx23885_s_host_register(dev, reg);
199 		case 1:
200 			return cx23417_s_register(dev, reg);
201 		default:
202 			break;
203 		}
204 	}
205 
206 	/* FIXME - any error returns should not be ignored */
207 	call_all(dev, core, s_register, reg);
208 	return 0;
209 }
210 #endif
211